").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/framework/yii/assets/jquery.maskedinput.js b/framework/yii/assets/jquery.maskedinput.js
index 49a5a72..902b5d3 100644
--- a/framework/yii/assets/jquery.maskedinput.js
+++ b/framework/yii/assets/jquery.maskedinput.js
@@ -9,7 +9,7 @@
var el = document.createElement('input'),
name = 'onpaste';
el.setAttribute(name, '');
- return (typeof el[name] === 'function')?'paste':'input';
+ return (typeof el[name] === 'function')?'paste':'input';
}
var pasteEventName = getPasteEvent() + ".mask",
@@ -322,9 +322,9 @@ $.fn.extend({
.bind("keydown.mask", keydownEvent)
.bind("keypress.mask", keypressEvent)
.bind(pasteEventName, function() {
- setTimeout(function() {
+ setTimeout(function() {
var pos=checkVal(true);
- input.caret(pos);
+ input.caret(pos);
if (settings.completed && pos == input.val().length)
settings.completed.call(input);
}, 0);
diff --git a/framework/yii/assets/jquery.min.js b/framework/yii/assets/jquery.min.js
deleted file mode 100644
index 006e953..0000000
--- a/framework/yii/assets/jquery.min.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
-//@ sourceMappingURL=jquery.min.map
-*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a ",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="
",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;
-return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="
";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="
",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="
",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="
",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="
",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="
",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/
\s*$/g,At={option:[1,""," "],legend:[1,""," "],area:[1,""," "],param:[1,""," "],thead:[1,""],tr:[2,""],col:[2,""],td:[3,""],_default:b.support.htmlSerialize?[0,"",""]:[1,"X","
"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l)
-}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write(""),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],"display");return n.remove(),r}b.each(["height","width"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,"display"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===b.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each(["top","left"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+"px":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||b.css(e,"display"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:"",padding:"",border:"Width"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,"elements");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==b.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}b.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=a.href}catch(Ln){yn=o.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o["*"]&&s("*")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&b.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?b("").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each(["get","post"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,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"},converters:{"* text":e.String,"text html":!0,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks("once memory"),m=p.statusCode||{},y={},v={},x=0,T="canceled",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||"*").toLowerCase().match(w)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?80:443))==(mn[3]||("http:"===mn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&N.setRequestHeader("If-None-Match",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader("Content-Type",p.contentType),N.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T="abort";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger("ajaxSend",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort("timeout")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||"",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader("Last-Modified"),T&&(b.lastModified[o]=T),T=N.getResponseHeader("etag"),T&&(b.etag[o]=T)),204===e?(c=!0,C="nocontent"):304===e?(c=!0,C="notmodified"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C="error",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+"",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?"ajaxSuccess":"ajaxError",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger("ajaxComplete",[N,p]),--b.active||b.event.trigger("ajaxStop")))}return N},getScript:function(e,n){return b.get(e,t,n,"script")},getJSON:function(e,t,n){return b.get(e,t,n,"json")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while("*"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+" "+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if("*"!==r){if("*"!==l&&l!==r){if(i=a[l+" "+r]||a["* "+r],!i)for(n in a)if(o=n.split(" "),o[1]===r&&(i=a[l+" "+o[0]]||a["* "+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e["throws"])t=i(t);else try{t=i(t)}catch(c){return{state:"parsererror",error:i?c:"No conversion from "+l+" to "+r}}}l=r}return{state:"success",data:t}}b.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),b.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=o.head||b("head")[0]||o.documentElement;return{send:function(t,i){n=o.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\?(?=&|$)|\?\?/;b.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=On.pop()||b.expando+"_"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||b.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&"withCredentials"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),"string"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+x+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?"":"px"),"px"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn["*"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&"expand"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===b.css(e,"display")&&"none"===b.css(e,"float")&&(b.support.inlineBlockNeedsLayout&&"inline"!==un(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||"toggle"===a,a===(m?"hide":"show"))continue;g.push(i)}if(o=g.length){s=b._data(e,"fxshow")||b._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,"fxshow");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each(["toggle","show","hide"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=b._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&"object"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,"position");"static"===r&&(e.style.position="relative");var i=b(e),o=i.offset(),a=b.css(e,"top"),s=b.css(e,"left"),u=("absolute"===r||"fixed"===r)&&b.inArray("auto",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),"using"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===b.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],"html")||(n=e.offset()),n.top+=b.css(e[0],"borderTopWidth",!0),n.left+=b.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-b.css(r,"marginTop",!0),left:t.left-n.left-b.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,"html")&&"static"===b.css(e,"position"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:"height",Width:"width"},function(e,n){b.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return b})})(window);
\ No newline at end of file
diff --git a/framework/yii/assets/yii.activeForm.js b/framework/yii/assets/yii.activeForm.js
index 1a2e58d..e898efc 100644
--- a/framework/yii/assets/yii.activeForm.js
+++ b/framework/yii/assets/yii.activeForm.js
@@ -41,6 +41,9 @@
// a callback that is called before validating each attribute. The signature of the callback should be:
// function ($form, attribute, messages) { ...return false to cancel the validation...}
beforeValidate: undefined,
+ // a callback that is called after an attribute is validated. The signature of the callback should be:
+ // function ($form, attribute, messages)
+ afterValidate: undefined,
// the GET parameter name indicating an AJAX-based validation
ajaxVar: 'ajax'
};
@@ -80,7 +83,7 @@
var settings = $.extend({}, defaults, options || {});
if (settings.validationUrl === undefined) {
- settings.validationUrl = $form.attr('action');
+ settings.validationUrl = $form.prop('action');
}
$.each(attributes, function (i) {
attributes[i] = $.extend({value: getValue($form, this)}, attributeDefaults, this);
@@ -125,7 +128,6 @@
data = $form.data('yiiActiveForm');
if (data.validated) {
// continue submitting the form since validation passes
- data.validated = false;
return true;
}
@@ -288,13 +290,13 @@
// If the validation is triggered by form submission, ajax validation
// should be done only when all inputs pass client validation
var $button = data.submitObject,
- extData = '&' + data.settings.ajaxVar + '=' + $form.attr('id');
- if ($button && $button.length && $button.attr('name')) {
- extData += '&' + $button.attr('name') + '=' + $button.attr('value');
+ extData = '&' + data.settings.ajaxVar + '=' + $form.prop('id');
+ if ($button && $button.length && $button.prop('name')) {
+ extData += '&' + $button.prop('name') + '=' + $button.prop('value');
}
$.ajax({
url: data.settings.validationUrl,
- type: $form.attr('method'),
+ type: $form.prop('method'),
data: $form.serialize() + extData,
dataType: 'json',
success: function (msgs) {
@@ -333,17 +335,20 @@
$input = findInput($form, attribute),
hasError = false;
+ if (data.settings.afterValidate) {
+ data.settings.afterValidate($form, attribute, messages);
+ }
attribute.status = 1;
if ($input.length) {
hasError = messages && $.isArray(messages[attribute.name]) && messages[attribute.name].length;
var $container = $form.find(attribute.container);
var $error = $container.find(attribute.error);
if (hasError) {
- $error.html(messages[attribute.name][0]);
+ $error.text(messages[attribute.name][0]);
$container.removeClass(data.settings.validatingCssClass + ' ' + data.settings.successCssClass)
.addClass(data.settings.errorCssClass);
} else {
- $error.html('');
+ $error.text('');
$container.removeClass(data.settings.validatingCssClass + ' ' + data.settings.errorCssClass + ' ')
.addClass(data.settings.successCssClass);
}
@@ -360,21 +365,21 @@
var updateSummary = function ($form, messages) {
var data = $form.data('yiiActiveForm'),
$summary = $form.find(data.settings.errorSummary),
- content = '';
+ $ul = $summary.find('ul');
if ($summary.length && messages) {
$.each(data.attributes, function () {
if ($.isArray(messages[this.name]) && messages[this.name].length) {
- content += '
' + messages[this.name][0] + ' ';
+ $ul.append($('
').text(messages[this.name][0]));
}
});
- $summary.toggle(content !== '').find('ul').html(content);
+ $summary.toggle($ul.find('li').length > 0);
}
};
var getValue = function ($form, attribute) {
var $input = findInput($form, attribute);
- var type = $input.attr('type');
+ var type = $input.prop('type');
if (type === 'checkbox' || type === 'radio') {
return $input.filter(':checked').val();
} else {
diff --git a/framework/yii/assets/yii.captcha.js b/framework/yii/assets/yii.captcha.js
index 7dfe3cc..af14faa 100644
--- a/framework/yii/assets/yii.captcha.js
+++ b/framework/yii/assets/yii.captcha.js
@@ -1,7 +1,7 @@
/**
* Yii Captcha widget.
*
- * This is the JavaScript widget used by the yii\widgets\Captcha widget.
+ * This is the JavaScript widget used by the yii\captcha\Captcha widget.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
diff --git a/framework/yii/assets/yii.debug.js b/framework/yii/assets/yii.debug.js
deleted file mode 100644
index e0d30f6..0000000
--- a/framework/yii/assets/yii.debug.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Yii debug module.
- *
- * This JavaScript module provides the functions needed by the Yii debug toolbar.
- *
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- * @author Qiang Xue
- * @since 2.0
- */
-
-yii.debug = (function ($) {
- return {
- load: function (id, url) {
- $.ajax({
- url: url,
- //dataType: 'json',
- success: function(data) {
- var $e = $('#' + id);
- $e.html(data).show();
- }
- });
- }
- };
-})(jQuery);
diff --git a/framework/yii/assets/yii.gridView.js b/framework/yii/assets/yii.gridView.js
new file mode 100644
index 0000000..a452c17
--- /dev/null
+++ b/framework/yii/assets/yii.gridView.js
@@ -0,0 +1,139 @@
+/**
+ * Yii GridView widget.
+ *
+ * This is the JavaScript widget used by the yii\grid\GridView widget.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ * @author Qiang Xue
+ * @since 2.0
+ */
+(function ($) {
+ $.fn.yiiGridView = function (method) {
+ if (methods[method]) {
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+ } else if (typeof method === 'object' || !method) {
+ return methods.init.apply(this, arguments);
+ } else {
+ $.error('Method ' + method + ' does not exist on jQuery.yiiGridView');
+ return false;
+ }
+ };
+
+ var defaults = {
+ filterUrl: undefined,
+ filterSelector: undefined
+ };
+
+ var methods = {
+ init: function (options) {
+ return this.each(function () {
+ var $e = $(this);
+ var settings = $.extend({}, defaults, options || {});
+ $e.data('yiiGridView', {
+ settings: settings
+ });
+
+ var enterPressed = false;
+ $(document).on('change.yiiGridView keydown.yiiGridView', settings.filterSelector, function (event) {
+ if (event.type === 'keydown') {
+ if (event.keyCode !== 13) {
+ return; // only react to enter key
+ } else {
+ enterPressed = true;
+ }
+ } else {
+ // prevent processing for both keydown and change events
+ if (enterPressed) {
+ enterPressed = false;
+ return;
+ }
+ }
+ var data = $(settings.filterSelector).serialize();
+ var url = settings.filterUrl;
+ if (url.indexOf('?') >= 0) {
+ url += '&' + data;
+ } else {
+ url += '?' + data;
+ }
+ window.location.href = url;
+ return false;
+ });
+ });
+ },
+
+ setSelectionColumn: function (options) {
+ var $grid = $(this);
+ var data = $grid.data('yiiGridView');
+ data.selectionColumn = options.name;
+ if (!options.multiple) {
+ return;
+ }
+ $grid.on('click.yiiGridView', "input[name='" + options.checkAll + "']", function () {
+ $grid.find("input[name='" + options.name + "']:enabled").prop('checked', this.checked);
+ });
+ $grid.on('click.yiiGridView', "input[name='" + options.name + "']:enabled", function () {
+ var all = $grid.find("input[name='" + options.name + "']").length == $grid.find("input[name='" + options.name + "']:checked").length;
+ $grid.find("input[name='" + options.checkAll + "']").prop('checked', all);
+ });
+ },
+
+ getSelectedRows: function () {
+ var $grid = $(this);
+ var data = $grid.data('yiiGridView');
+ var keys = [];
+ if (data.selectionColumn) {
+ $grid.find("input[name='" + data.selectionColumn + "']:checked").each(function () {
+ keys.push($(this).parent().closest('tr').data('key'));
+ });
+ }
+ return keys;
+ },
+
+ destroy: function () {
+ return this.each(function () {
+ $(window).unbind('.yiiGridView');
+ $(this).removeData('yiiGridView');
+ });
+ },
+
+ data: function() {
+ return this.data('yiiGridView');
+ }
+ };
+
+ var enterPressed = false;
+
+ var filterChanged = function (event) {
+ if (event.type === 'keydown') {
+ if (event.keyCode !== 13) {
+ return; // only react to enter key
+ } else {
+ enterPressed = true;
+ }
+ } else {
+ // prevent processing for both keydown and change events
+ if (enterPressed) {
+ enterPressed = false;
+ return;
+ }
+ }
+ var data = $(settings.filterSelector).serialize();
+ if (settings.pageVar !== undefined) {
+ data += '&' + settings.pageVar + '=1';
+ }
+ if (settings.enableHistory && settings.ajaxUpdate !== false && window.History.enabled) {
+ // Ajaxify this link
+ var url = $('#' + id).yiiGridView('getUrl'),
+ params = $.deparam.querystring($.param.querystring(url, data));
+
+ delete params[settings.ajaxVar];
+ window.History.pushState(null, document.title, decodeURIComponent($.param.querystring(url.substr(0, url.indexOf('?')), params)));
+ } else {
+ $('#' + id).yiiGridView('update', {data: data});
+ }
+ return false;
+ };
+})(window.jQuery);
+
diff --git a/framework/yii/assets/yii.js b/framework/yii/assets/yii.js
index 1e847c4..c5904a1 100644
--- a/framework/yii/assets/yii.js
+++ b/framework/yii/assets/yii.js
@@ -32,18 +32,126 @@
* // ... private functions and properties go here ...
*
* return pub;
- * });
+ * })(jQuery);
* ~~~
*
* Using this structure, you can define public and private functions/properties for a module.
* Private functions/properties are only visible within the module, while public functions/properties
- * may be accessed outside of the module. For example, you can access "yii.sample.init()".
+ * may be accessed outside of the module. For example, you can access "yii.sample.isActive".
*
* You must call "yii.initModule()" once for the root module of all your modules.
*/
yii = (function ($) {
var pub = {
- version: '2.0',
+ /**
+ * List of scripts that can be loaded multiple times via AJAX requests. Each script can be represented
+ * as either an absolute URL or a relative one.
+ */
+ reloadableScripts: [],
+ /**
+ * The selector for clickable elements that need to support confirmation and form submission.
+ */
+ clickableSelector: 'a, button, input[type="submit"], input[type="button"], input[type="reset"], input[type="image"]',
+ /**
+ * The selector for changeable elements that need to support confirmation and form submission.
+ */
+ changeableSelector: 'select, input, textarea',
+
+ /**
+ * @return string|undefined the CSRF variable name. Undefined is returned if CSRF validation is not enabled.
+ */
+ getCsrfVar: function () {
+ return $('meta[name=csrf-var]').prop('content');
+ },
+
+ /**
+ * @return string|undefined the CSRF token. Undefined is returned if CSRF validation is not enabled.
+ */
+ getCsrfToken: function () {
+ return $('meta[name=csrf-token]').prop('content');
+ },
+
+ /**
+ * Displays a confirmation dialog.
+ * The default implementation simply displays a js confirmation dialog.
+ * You may override this by setting `yii.confirm`.
+ * @param message the confirmation message.
+ * @return boolean whether the user confirms with the message in the dialog
+ */
+ confirm: function (message) {
+ return confirm(message);
+ },
+
+ /**
+ * Returns a value indicating whether to allow executing the action defined for the specified element.
+ * This method recognizes the `data-confirm` attribute of the element and uses it
+ * as the message in a confirmation dialog. The method will return true if this special attribute
+ * is not defined or if the user confirms the message.
+ * @param $e the jQuery representation of the element
+ * @return boolean whether to allow executing the action defined for the specified element.
+ */
+ allowAction: function ($e) {
+ var message = $e.data('confirm');
+ return message === undefined || pub.confirm(message);
+ },
+
+ /**
+ * Handles the action triggered by user.
+ * This method recognizes the `data-method` attribute of the element. If the attribute exists,
+ * the method will submit the form containing this element. If there is no containing form, a form
+ * will be created and submitted using the method given by this attribute value (e.g. "post", "put").
+ * For hyperlinks, the form action will take the value of the "href" attribute of the link.
+ * For other elements, either the containing form action or the current page URL will be used
+ * as the form action URL.
+ *
+ * If the `data-method` attribute is not defined, the default element action will be performed.
+ *
+ * @param $e the jQuery representation of the element
+ * @return boolean whether to execute the default action for the element.
+ */
+ handleAction: function ($e) {
+ var method = $e.data('method');
+ if (method === undefined) {
+ return true;
+ }
+
+ var $form = $e.closest('form');
+ var newForm = !$form.length;
+ if (newForm) {
+ var action = $e.prop('href');
+ if (!action || !action.match(/(^\/|:\/\/)/)) {
+ action = window.location.href;
+ }
+ $form = $('');
+ var target = $e.prop('target');
+ if (target) {
+ $form.attr('target', target);
+ }
+ if (!method.match(/(get|post)/i)) {
+ $form.append(' ');
+ }
+ var csrfVar = pub.getCsrfVar();
+ if (csrfVar) {
+ $form.append(' ');
+ }
+ $form.hide().appendTo('body');
+ }
+
+ var activeFormData = $form.data('yiiActiveForm');
+ if (activeFormData) {
+ // remember who triggers the form submission. This is used by yii.activeForm.js
+ activeFormData.submitObject = $e;
+ }
+
+ $form.trigger('submit');
+
+ if (newForm) {
+ $form.remove();
+ }
+
+ return false;
+ },
+
initModule: function (module) {
if (module.isActive === undefined || module.isActive) {
if ($.isFunction(module.init)) {
@@ -55,8 +163,80 @@ yii = (function ($) {
}
});
}
+ },
+
+ init: function () {
+ initCsrfHandler();
+ initRedirectHandler();
+ initScriptFilter();
+ initDataMethods();
}
};
+
+ function initRedirectHandler() {
+ // handle AJAX redirection
+ $(document).ajaxComplete(function (event, xhr, settings) {
+ var url = xhr.getResponseHeader('X-Redirect');
+ if (url) {
+ window.location = url;
+ }
+ });
+ }
+
+ function initCsrfHandler() {
+ // automatically send CSRF token for all AJAX requests
+ $.ajaxPrefilter(function (options, originalOptions, xhr) {
+ if (!options.crossDomain && pub.getCsrfVar()) {
+ xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken());
+ }
+ });
+ }
+
+ function initDataMethods() {
+ var $document = $(document);
+ // handle data-confirm and data-method for clickable elements
+ $document.on('click.yii', pub.clickableSelector, function (event) {
+ var $this = $(this);
+ if (pub.allowAction($this)) {
+ return pub.handleAction($this);
+ } else {
+ event.stopImmediatePropagation();
+ return false;
+ }
+ });
+
+ // handle data-confirm and data-method for changeable elements
+ $document.on('change.yii', pub.changeableSelector, function (event) {
+ var $this = $(this);
+ if (pub.allowAction($this)) {
+ return pub.handleAction($this);
+ } else {
+ event.stopImmediatePropagation();
+ return false;
+ }
+ });
+ }
+
+ function initScriptFilter() {
+ var hostInfo = location.protocol + '//' + location.host;
+ var loadedScripts = $('script[src]').map(function () {
+ return this.src.charAt(0) === '/' ? hostInfo + this.src : this.src;
+ }).toArray();
+ $.ajaxPrefilter('script', function (options, originalOptions, xhr) {
+ var url = options.url.charAt(0) === '/' ? hostInfo + options.url : options.url;
+ if ($.inArray(url, loadedScripts) === -1) {
+ loadedScripts.push(url);
+ } else {
+ var found = $.inArray(url, $.map(pub.reloadableScripts, function (script) {
+ return script.charAt(0) === '/' ? hostInfo + script : script;
+ })) !== -1;
+ if (!found) {
+ xhr.abort();
+ }
+ }
+ });
+ }
+
return pub;
})(jQuery);
diff --git a/framework/yii/assets/yii.validation.js b/framework/yii/assets/yii.validation.js
index 015040e..97074ac 100644
--- a/framework/yii/assets/yii.validation.js
+++ b/framework/yii/assets/yii.validation.js
@@ -16,6 +16,10 @@ yii.validation = (function ($) {
|| value === '' || trim && $.trim(value) === '';
};
+ var addMessage = function (messages, message, value) {
+ messages.push(message.replace(/\{value\}/g, value));
+ };
+
return {
required: function (value, messages, options) {
var valid = false;
@@ -28,7 +32,7 @@ yii.validation = (function ($) {
}
if (!valid) {
- messages.push(options.message);
+ addMessage(messages, options.message, value);
}
},
@@ -40,7 +44,7 @@ yii.validation = (function ($) {
|| options.strict && (value === options.trueValue || value === options.falseValue);
if (!valid) {
- messages.push(options.message);
+ addMessage(messages, options.message, value);
}
},
@@ -50,18 +54,18 @@ yii.validation = (function ($) {
}
if (typeof value !== 'string') {
- messages.push(options.message);
+ addMessage(messages, options.message, value);
return;
}
if (options.min !== undefined && value.length < options.min) {
- messages.push(options.tooShort);
+ addMessage(messages, options.tooShort, value);
}
if (options.max !== undefined && value.length > options.max) {
- messages.push(options.tooLong);
+ addMessage(messages, options.tooLong, value);
}
if (options.is !== undefined && value.length != options.is) {
- messages.push(options.is);
+ addMessage(messages, options.is, value);
}
},
@@ -71,15 +75,15 @@ yii.validation = (function ($) {
}
if (typeof value === 'string' && !value.match(options.pattern)) {
- messages.push(options.message);
+ addMessage(messages, options.message, value);
return;
}
if (options.min !== undefined && value < options.min) {
- messages.push(options.tooSmall);
+ addMessage(messages, options.tooSmall, value);
}
if (options.max !== undefined && value > options.max) {
- messages.push(options.tooBig);
+ addMessage(messages, options.tooBig, value);
}
},
@@ -91,7 +95,7 @@ yii.validation = (function ($) {
|| options.not && $.inArray(value, options.range) == -1;
if (!valid) {
- messages.push(options.message);
+ addMessage(messages, options.message, value);
}
},
@@ -101,7 +105,7 @@ yii.validation = (function ($) {
}
if (!options.not && !value.match(options.pattern) || options.not && value.match(options.pattern)) {
- messages.push(options.message)
+ addMessage(messages, options.message, value);
}
},
@@ -113,17 +117,17 @@ yii.validation = (function ($) {
var valid = true;
if (options.enableIDN) {
- var regexp = /^(.*)@(.*)$/,
+ var regexp = /^(.*)(.*)@(.*)(>?)$/,
matches = regexp.exec(value);
if (matches === null) {
valid = false;
} else {
- value = punycode.toASCII(matches[1]) + '@' + punycode.toASCII(matches[2]);
+ value = matches[1] + punycode.toASCII(matches[2]) + '@' + punycode.toASCII(matches[3]) + matches[4];
}
}
- if (!valid || !(value.match(options.pattern) && (!options.allowName || value.match(options.fullPattern)))) {
- messages.push(options.message);
+ if (!valid || !(value.match(options.pattern) || (options.allowName && value.match(options.fullPattern)))) {
+ addMessage(messages, options.message, value);
}
},
@@ -149,7 +153,7 @@ yii.validation = (function ($) {
}
if (!valid || !value.match(options.pattern)) {
- messages.push(options.message);
+ addMessage(messages, options.message, value);
}
},
@@ -170,7 +174,7 @@ yii.validation = (function ($) {
h += v.charCodeAt(i);
}
if (h != hash) {
- messages.push(options.message);
+ addMessage(messages, options.message, value);
}
},
@@ -210,10 +214,13 @@ yii.validation = (function ($) {
case '<=':
valid = value <= compareValue;
break;
+ default:
+ valid = false;
+ break;
}
if (!valid) {
- messages.push(options.message);
+ addMessage(messages, options.message, value);
}
}
};
diff --git a/framework/yii/base/Action.php b/framework/yii/base/Action.php
index 7142539..56b1c45 100644
--- a/framework/yii/base/Action.php
+++ b/framework/yii/base/Action.php
@@ -7,6 +7,8 @@
namespace yii\base;
+use Yii;
+
/**
* Action is the base class for all controller action classes.
*
@@ -23,9 +25,12 @@ namespace yii\base;
* public function run($id, $type = 'book') { ... }
* ~~~
*
- * And the parameters provided for the action are: `array('id' => 1)`.
+ * And the parameters provided for the action are: `['id' => 1]`.
* Then the `run()` method will be invoked as `run(1)` automatically.
*
+ * @property string $uniqueId The unique ID of this action among the whole application. This property is
+ * read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -46,7 +51,7 @@ class Action extends Component
* @param Controller $controller the controller that owns this action
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($id, $controller, $config = array())
+ public function __construct($id, $controller, $config = [])
{
$this->id = $id;
$this->controller = $controller;
@@ -66,7 +71,7 @@ class Action extends Component
* Runs this action with the specified parameters.
* This method is mainly invoked by the controller.
* @param array $params the parameters to be bound to the action's run() method.
- * @return integer the exit status (0 means normal, non-zero means abnormal).
+ * @return mixed the result of the action
* @throws InvalidConfigException if the action class does not have a run() method
*/
public function runWithParams($params)
@@ -75,6 +80,10 @@ class Action extends Component
throw new InvalidConfigException(get_class($this) . ' must define a "run()" method.');
}
$args = $this->controller->bindActionParams($this, $params);
- return (int)call_user_func_array(array($this, 'run'), $args);
+ Yii::trace('Running action: ' . get_class($this) . '::run()', __METHOD__);
+ if (Yii::$app->requestedParams === null) {
+ Yii::$app->requestedParams = $args;
+ }
+ return call_user_func_array([$this, 'run'], $args);
}
}
diff --git a/framework/yii/base/ActionEvent.php b/framework/yii/base/ActionEvent.php
index 9507b12..6e123a0 100644
--- a/framework/yii/base/ActionEvent.php
+++ b/framework/yii/base/ActionEvent.php
@@ -22,6 +22,10 @@ class ActionEvent extends Event
*/
public $action;
/**
+ * @var mixed the action result. Event handlers may modify this property to change the action result.
+ */
+ public $result;
+ /**
* @var boolean whether to continue running the action. Event handlers of
* [[Controller::EVENT_BEFORE_ACTION]] may set this property to decide whether
* to continue running the current action.
@@ -33,7 +37,7 @@ class ActionEvent extends Event
* @param Action $action the action associated with this action event.
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($action, $config = array())
+ public function __construct($action, $config = [])
{
$this->action = $action;
parent::__construct($config);
diff --git a/framework/yii/base/ActionFilter.php b/framework/yii/base/ActionFilter.php
index 20ff142..648211c 100644
--- a/framework/yii/base/ActionFilter.php
+++ b/framework/yii/base/ActionFilter.php
@@ -8,6 +8,11 @@
namespace yii\base;
/**
+ * ActionFilter provides a base implementation for action filters that can be added to a controller
+ * to handle the `beforeAction` event.
+ *
+ * Check implementation of [[AccessControl]], [[PageCache]] and [[HttpCache]] as examples on how to use it.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -16,12 +21,15 @@ class ActionFilter extends Behavior
/**
* @var array list of action IDs that this filter should apply to. If this property is not set,
* then the filter applies to all actions, unless they are listed in [[except]].
+ * If an action ID appears in both [[only]] and [[except]], this filter will NOT apply to it.
+ * @see except
*/
public $only;
/**
* @var array list of action IDs that this filter should not apply to.
+ * @see only
*/
- public $except = array();
+ public $except = [];
/**
* Declares event handlers for the [[owner]]'s events.
@@ -29,10 +37,10 @@ class ActionFilter extends Behavior
*/
public function events()
{
- return array(
+ return [
Controller::EVENT_BEFORE_ACTION => 'beforeFilter',
Controller::EVENT_AFTER_ACTION => 'afterFilter',
- );
+ ];
}
/**
@@ -54,7 +62,7 @@ class ActionFilter extends Behavior
public function afterFilter($event)
{
if ($this->isActive($event->action)) {
- $this->afterAction($event->action);
+ $this->afterAction($event->action, $event->result);
}
}
@@ -73,8 +81,9 @@ class ActionFilter extends Behavior
* This method is invoked right after an action is executed.
* You may override this method to do some postprocessing for the action.
* @param Action $action the action just executed.
+ * @param mixed $result the action execution result
*/
- public function afterAction($action)
+ public function afterAction($action, &$result)
{
}
diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php
index f5f3d6a..32e63da 100644
--- a/framework/yii/base/Application.php
+++ b/framework/yii/base/Application.php
@@ -8,18 +8,63 @@
namespace yii\base;
use Yii;
+use yii\helpers\Console;
+use yii\web\HttpException;
/**
* Application is the base class for all application classes.
*
+ * @property \yii\rbac\Manager $authManager The auth manager for this application. This property is read-only.
+ * @property string $basePath The root directory of the application.
+ * @property \yii\caching\Cache $cache The cache application component. Null if the component is not enabled.
+ * This property is read-only.
+ * @property \yii\db\Connection $db The database connection. This property is read-only.
+ * @property ErrorHandler $errorHandler The error handler application component. This property is read-only.
+ * @property \yii\base\Formatter $formatter The formatter application component. This property is read-only.
+ * @property \yii\i18n\I18N $i18n The internationalization component. This property is read-only.
+ * @property \yii\log\Logger $log The log component. This property is read-only.
+ * @property \yii\mail\MailerInterface $mail The mailer interface. This property is read-only.
+ * @property \yii\web\Request|\yii\console\Request $request The request component. This property is read-only.
+ * @property string $runtimePath The directory that stores runtime files. Defaults to the "runtime"
+ * subdirectory under [[basePath]].
+ * @property string $timeZone The time zone used by this application.
+ * @property string $uniqueId The unique ID of the module. This property is read-only.
+ * @property \yii\web\UrlManager $urlManager The URL manager for this application. This property is read-only.
+ * @property string $vendorPath The directory that stores vendor files. Defaults to "vendor" directory under
+ * [[basePath]].
+ * @property View|\yii\web\View $view The view object that is used to render various view files. This property
+ * is read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
-class Application extends Module
+abstract class Application extends Module
{
+ /**
+ * @event Event an event raised before the application starts to handle a request.
+ */
const EVENT_BEFORE_REQUEST = 'beforeRequest';
+ /**
+ * @event Event an event raised after the application successfully handles a request (before the response is sent out).
+ */
const EVENT_AFTER_REQUEST = 'afterRequest';
/**
+ * @event ActionEvent an event raised before executing a controller action.
+ * You may set [[ActionEvent::isValid]] to be false to cancel the action execution.
+ */
+ const EVENT_BEFORE_ACTION = 'beforeAction';
+ /**
+ * @event ActionEvent an event raised after executing a controller action.
+ */
+ const EVENT_AFTER_ACTION = 'afterAction';
+
+ /**
+ * @var string the namespace that controller classes are in. If not set,
+ * it will use the "app\controllers" namespace.
+ */
+ public $controllerNamespace = 'app\\controllers';
+
+ /**
* @var string the application name.
*/
public $name = 'My Application';
@@ -35,28 +80,59 @@ class Application extends Module
* @var string the language that is meant to be used for end users.
* @see sourceLanguage
*/
- public $language = 'en_US';
+ public $language = 'en-US';
/**
* @var string the language that the application is written in. This mainly refers to
* the language that the messages and view files are written in.
* @see language
*/
- public $sourceLanguage = 'en_US';
- /**
- * @var array IDs of the components that need to be loaded when the application starts.
- */
- public $preload = array();
+ public $sourceLanguage = 'en-US';
/**
- * @var \yii\web\Controller|\yii\console\Controller the currently active controller instance
+ * @var Controller the currently active controller instance
*/
public $controller;
/**
- * @var mixed the layout that should be applied for views in this application. Defaults to 'main'.
+ * @var string|boolean the layout that should be applied for views in this application. Defaults to 'main'.
* If this is false, layout will be disabled.
*/
public $layout = 'main';
-
- private $_ended = false;
+ /**
+ * @var integer the size of the reserved memory. A portion of memory is pre-allocated so that
+ * when an out-of-memory issue occurs, the error handler is able to handle the error with
+ * the help of this reserved memory. If you set this value to be 0, no memory will be reserved.
+ * Defaults to 256KB.
+ */
+ public $memoryReserveSize = 262144;
+ /**
+ * @var string the requested route
+ */
+ public $requestedRoute;
+ /**
+ * @var Action the requested Action. If null, it means the request cannot be resolved into an action.
+ */
+ public $requestedAction;
+ /**
+ * @var array the parameters supplied to the requested action.
+ */
+ public $requestedParams;
+ /**
+ * @var array list of installed Yii extensions. Each array element represents a single extension
+ * with the following structure:
+ *
+ * ~~~
+ * [
+ * 'name' => 'extension name',
+ * 'version' => 'version number',
+ * 'bootstrap' => 'BootstrapClassName',
+ * ]
+ * ~~~
+ */
+ public $extensions = [];
+ /**
+ * @var \Exception the exception that is being handled currently. When this is not null,
+ * it means the application is handling some exception and extra care should be taken.
+ */
+ public $exception;
/**
* @var string Used to reserve memory for fatal error handler.
@@ -69,21 +145,11 @@ class Application extends Module
* Note that the configuration must contain both [[id]] and [[basePath]].
* @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.
*/
- public function __construct($config = array())
+ public function __construct($config = [])
{
Yii::$app = $this;
- if (!isset($config['id'])) {
- throw new InvalidConfigException('The "id" configuration is required.');
- }
- if (isset($config['basePath'])) {
- $this->setBasePath($config['basePath']);
- unset($config['basePath']);
- } else {
- throw new InvalidConfigException('The "basePath" configuration is required.');
- }
$this->preInit($config);
-
$this->registerErrorHandlers();
$this->registerCoreComponents();
@@ -93,10 +159,23 @@ class Application extends Module
/**
* Pre-initializes the application.
* This method is called at the beginning of the application constructor.
+ * It initializes several important application properties.
+ * If you override this method, please make sure you call the parent implementation.
* @param array $config the application configuration
+ * @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.
*/
public function preInit(&$config)
{
+ if (!isset($config['id'])) {
+ throw new InvalidConfigException('The "id" configuration is required.');
+ }
+ if (isset($config['basePath'])) {
+ $this->setBasePath($config['basePath']);
+ unset($config['basePath']);
+ } else {
+ throw new InvalidConfigException('The "basePath" configuration is required.');
+ }
+
if (isset($config['vendorPath'])) {
$this->setVendorPath($config['vendorPath']);
unset($config['vendorPath']);
@@ -111,6 +190,7 @@ class Application extends Module
// set "@runtime"
$this->getRuntimePath();
}
+
if (isset($config['timeZone'])) {
$this->setTimeZone($config['timeZone']);
unset($config['timeZone']);
@@ -120,84 +200,110 @@ class Application extends Module
}
/**
- * Registers error handlers.
+ * @inheritdoc
*/
- public function registerErrorHandlers()
+ public function init()
{
- if (YII_ENABLE_ERROR_HANDLER) {
- ini_set('display_errors', 0);
- set_exception_handler(array($this, 'handleException'));
- set_error_handler(array($this, 'handleError'), error_reporting());
- // Allocating twice more than required to display memory exhausted error
- // in case of trying to allocate last 1 byte while all memory is taken.
- $this->_memoryReserve = str_repeat('x', 1024 * 256);
- register_shutdown_function(array($this, 'end'), 0, false);
- register_shutdown_function(array($this, 'handleFatalError'));
- }
+ $this->initExtensions($this->extensions);
+ parent::init();
}
/**
- * Terminates the application.
- * This method replaces PHP's exit() function by calling [[afterRequest()]] before exiting.
- * @param integer $status exit status (value 0 means normal exit while other values mean abnormal exit).
- * @param boolean $exit whether to exit the current request.
- * It defaults to true, meaning the PHP's exit() function will be called at the end of this method.
+ * Initializes the extensions.
+ * @param array $extensions the extensions to be initialized. Please refer to [[extensions]]
+ * for the structure of the extension array.
*/
- public function end($status = 0, $exit = true)
+ protected function initExtensions($extensions)
{
- if (!$this->_ended) {
- $this->_ended = true;
- $this->getResponse()->end();
- $this->afterRequest();
+ foreach ($extensions as $extension) {
+ if (!empty($extension['alias'])) {
+ foreach ($extension['alias'] as $name => $path) {
+ Yii::setAlias($name, $path);
+ }
+ }
+ if (isset($extension['bootstrap'])) {
+ /** @var Extension $class */
+ $class = $extension['bootstrap'];
+ $class::init();
+ }
}
+ }
- if ($exit) {
- exit($status);
- }
+ /**
+ * Loads components that are declared in [[preload]].
+ * @throws InvalidConfigException if a component or module to be preloaded is unknown
+ */
+ public function preloadComponents()
+ {
+ $this->getComponent('log');
+ parent::preloadComponents();
}
/**
- * Runs the application.
- * This is the main entrance of an application.
- * @return integer the exit status (0 means normal, non-zero values mean abnormal)
+ * Registers error handlers.
*/
- public function run()
+ public function registerErrorHandlers()
{
- $this->beforeRequest();
- $response = $this->getResponse();
- $response->begin();
- $status = $this->processRequest();
- $response->end();
- $this->afterRequest();
- return $status;
+ if (YII_ENABLE_ERROR_HANDLER) {
+ ini_set('display_errors', 0);
+ set_exception_handler([$this, 'handleException']);
+ set_error_handler([$this, 'handleError'], error_reporting());
+ if ($this->memoryReserveSize > 0) {
+ $this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);
+ }
+ register_shutdown_function([$this, 'handleFatalError']);
+ }
}
/**
- * Raises the [[EVENT_BEFORE_REQUEST]] event right BEFORE the application processes the request.
+ * Returns an ID that uniquely identifies this module among all modules within the current application.
+ * Since this is an application instance, it will always return an empty string.
+ * @return string the unique ID of the module.
*/
- public function beforeRequest()
+ public function getUniqueId()
{
- $this->trigger(self::EVENT_BEFORE_REQUEST);
+ return '';
}
/**
- * Raises the [[EVENT_AFTER_REQUEST]] event right AFTER the application processes the request.
+ * Sets the root directory of the application and the @app alias.
+ * This method can only be invoked at the beginning of the constructor.
+ * @param string $path the root directory of the application.
+ * @property string the root directory of the application.
+ * @throws InvalidParamException if the directory does not exist.
*/
- public function afterRequest()
+ public function setBasePath($path)
{
- $this->trigger(self::EVENT_AFTER_REQUEST);
+ parent::setBasePath($path);
+ Yii::setAlias('@app', $this->getBasePath());
}
/**
- * Processes the request.
- * Child classes should override this method with actual request processing logic.
- * @return integer the exit status of the controller action (0 means normal, non-zero values mean abnormal)
+ * Runs the application.
+ * This is the main entrance of an application.
+ * @return integer the exit status (0 means normal, non-zero values mean abnormal)
*/
- public function processRequest()
+ public function run()
{
- return 0;
+ $this->trigger(self::EVENT_BEFORE_REQUEST);
+ $response = $this->handleRequest($this->getRequest());
+ $this->trigger(self::EVENT_AFTER_REQUEST);
+ $response->send();
+ return $response->exitStatus;
}
+ /**
+ * Handles the specified request.
+ *
+ * This method should return an instance of [[Response]] or its child class
+ * which represents the handling result of the request.
+ *
+ * @param Request $request the request to be handled
+ * @return Response the resulting response
+ */
+ abstract public function handleRequest($request);
+
+
private $_runtimePath;
/**
@@ -264,6 +370,7 @@ class Application extends Module
/**
* Sets the time zone used by this application.
* This is a simple wrapper of PHP function date_default_timezone_set().
+ * Refer to the [php manual](http://www.php.net/manual/en/timezones.php) for available timezones.
* @param string $value the time zone used by this application.
* @see http://php.net/manual/en/function.date-default-timezone-set.php
*/
@@ -282,6 +389,15 @@ class Application extends Module
}
/**
+ * Returns the log component.
+ * @return \yii\log\Logger the log component
+ */
+ public function getLog()
+ {
+ return $this->getComponent('log');
+ }
+
+ /**
* Returns the error handler component.
* @return ErrorHandler the error handler application component.
*/
@@ -318,17 +434,8 @@ class Application extends Module
}
/**
- * Returns the response component.
- * @return \yii\web\Response|\yii\console\Response the response component
- */
- public function getResponse()
- {
- return $this->getComponent('response');
- }
-
- /**
* Returns the view object.
- * @return View the view object that is used to render various view files.
+ * @return View|\yii\web\View the view object that is used to render various view files.
*/
public function getView()
{
@@ -348,12 +455,21 @@ class Application extends Module
* Returns the internationalization (i18n) component
* @return \yii\i18n\I18N the internationalization component
*/
- public function getI18N()
+ public function getI18n()
{
return $this->getComponent('i18n');
}
/**
+ * Returns the mailer component.
+ * @return \yii\mail\MailerInterface the mailer interface
+ */
+ public function getMail()
+ {
+ return $this->getComponent('mail');
+ }
+
+ /**
* Returns the auth manager for this application.
* @return \yii\rbac\Manager the auth manager for this application.
*/
@@ -368,57 +484,48 @@ class Application extends Module
*/
public function registerCoreComponents()
{
- $this->setComponents(array(
- 'errorHandler' => array(
- 'class' => 'yii\base\ErrorHandler',
- ),
- 'formatter' => array(
- 'class' => 'yii\base\Formatter',
- ),
- 'i18n' => array(
- 'class' => 'yii\i18n\I18N',
- ),
- 'urlManager' => array(
- 'class' => 'yii\web\UrlManager',
- ),
- 'view' => array(
- 'class' => 'yii\base\View',
- ),
- ));
+ $this->setComponents([
+ 'log' => ['class' => 'yii\log\Logger'],
+ 'errorHandler' => ['class' => 'yii\base\ErrorHandler'],
+ 'formatter' => ['class' => 'yii\base\Formatter'],
+ 'i18n' => ['class' => 'yii\i18n\I18N'],
+ 'urlManager' => ['class' => 'yii\web\UrlManager'],
+ 'view' => ['class' => 'yii\web\View'],
+ ]);
}
/**
* Handles uncaught PHP exceptions.
*
- * This method is implemented as a PHP exception handler. It requires
- * that constant YII_ENABLE_ERROR_HANDLER be defined true.
+ * This method is implemented as a PHP exception handler.
*
- * @param \Exception $exception exception that is not caught
+ * @param \Exception $exception the exception that is not caught
*/
public function handleException($exception)
{
+ $this->exception = $exception;
+
// disable error capturing to avoid recursive errors while handling exceptions
restore_error_handler();
restore_exception_handler();
-
try {
$this->logException($exception);
-
if (($handler = $this->getErrorHandler()) !== null) {
$handler->handle($exception);
} else {
- $this->renderException($exception);
+ echo $this->renderException($exception);
}
-
- $this->end(1);
-
} catch (\Exception $e) {
- // exception could be thrown in end() or ErrorHandler::handle()
+ // exception could be thrown in ErrorHandler::handle()
$msg = (string)$e;
$msg .= "\nPrevious exception:\n";
$msg .= (string)$exception;
if (YII_DEBUG) {
- echo $msg;
+ if (PHP_SAPI === 'cli') {
+ echo $msg . "\n";
+ } else {
+ echo '' . htmlspecialchars($msg, ENT_QUOTES, $this->charset) . ' ';
+ }
}
$msg .= "\n\$_SERVER = " . var_export($_SERVER, true);
error_log($msg);
@@ -441,6 +548,14 @@ class Application extends Module
public function handleError($code, $message, $file, $line)
{
if (error_reporting() !== 0) {
+ // load ErrorException manually here because autoloading them will not work
+ // when error occurs while autoloading a class
+ if (!class_exists('\\yii\\base\\Exception', false)) {
+ require_once(__DIR__ . '/Exception.php');
+ }
+ if (!class_exists('\\yii\\base\\ErrorException', false)) {
+ require_once(__DIR__ . '/ErrorException.php');
+ }
$exception = new ErrorException($message, $code, $code, $file, $line);
// in case error appeared in __toString method we can't throw any exception
@@ -449,6 +564,7 @@ class Application extends Module
foreach ($trace as $frame) {
if ($frame['function'] == '__toString') {
$this->handleException($exception);
+ exit(1);
}
}
@@ -461,54 +577,68 @@ class Application extends Module
*/
public function handleFatalError()
{
- if (YII_ENABLE_ERROR_HANDLER) {
- $error = error_get_last();
+ unset($this->_memoryReserve);
+
+ // load ErrorException manually here because autoloading them will not work
+ // when error occurs while autoloading a class
+ if (!class_exists('\\yii\\base\\Exception', false)) {
+ require_once(__DIR__ . '/Exception.php');
+ }
+ if (!class_exists('\\yii\\base\\ErrorException', false)) {
+ require_once(__DIR__ . '/ErrorException.php');
+ }
- if (ErrorException::isFatalError($error)) {
- unset($this->_memoryReserve);
- $exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
- // use error_log because it's too late to use Yii log
- error_log($exception);
+ $error = error_get_last();
- if (($handler = $this->getErrorHandler()) !== null) {
- $handler->handle($exception);
- } else {
- $this->renderException($exception);
- }
+ if (ErrorException::isFatalError($error)) {
+ $exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
+ $this->exception = $exception;
+ // use error_log because it's too late to use Yii log
+ error_log($exception);
- exit(1);
+ if (($handler = $this->getErrorHandler()) !== null) {
+ $handler->handle($exception);
+ } else {
+ echo $this->renderException($exception);
}
+
+ exit(1);
}
}
/**
* Renders an exception without using rich format.
* @param \Exception $exception the exception to be rendered.
+ * @return string the rendering result
*/
public function renderException($exception)
{
if ($exception instanceof Exception && ($exception instanceof UserException || !YII_DEBUG)) {
$message = $exception->getName() . ': ' . $exception->getMessage();
+ if (Yii::$app->controller instanceof \yii\console\Controller) {
+ $message = Yii::$app->controller->ansiFormat($message, Console::FG_RED);
+ }
} else {
$message = YII_DEBUG ? (string)$exception : 'Error: ' . $exception->getMessage();
}
- if (PHP_SAPI) {
- echo $message . "\n";
+ if (PHP_SAPI === 'cli') {
+ return $message . "\n";
} else {
- echo '' . htmlspecialchars($message, ENT_QUOTES, $this->charset) . ' ';
+ return '' . htmlspecialchars($message, ENT_QUOTES, $this->charset) . ' ';
}
}
- // todo: to be polished
+ /**
+ * Logs the given exception
+ * @param \Exception $exception the exception to be logged
+ */
protected function logException($exception)
{
$category = get_class($exception);
if ($exception instanceof HttpException) {
- /** @var $exception HttpException */
- $category .= '\\' . $exception->statusCode;
+ $category = 'yii\\web\\HttpException:' . $exception->statusCode;
} elseif ($exception instanceof \ErrorException) {
- /** @var $exception \ErrorException */
- $category .= '\\' . $exception->getSeverity();
+ $category .= ':' . $exception->getSeverity();
}
Yii::error((string)$exception, $category);
}
diff --git a/framework/yii/base/Arrayable.php b/framework/yii/base/Arrayable.php
new file mode 100644
index 0000000..1822e51
--- /dev/null
+++ b/framework/yii/base/Arrayable.php
@@ -0,0 +1,23 @@
+
+ * @since 2.0
+ */
+interface Arrayable
+{
+ /**
+ * Converts the object into an array.
+ * @return array the array representation of this object
+ */
+ public function toArray();
+}
diff --git a/framework/yii/base/Behavior.php b/framework/yii/base/Behavior.php
index abe08bb..1443e06 100644
--- a/framework/yii/base/Behavior.php
+++ b/framework/yii/base/Behavior.php
@@ -37,25 +37,25 @@ class Behavior extends \yii\base\Object
*
* The callbacks can be any of the followings:
*
- * - method in this behavior: `'handleClick'`, equivalent to `array($this, 'handleClick')`
- * - object method: `array($object, 'handleClick')`
- * - static method: `array('Page', 'handleClick')`
+ * - method in this behavior: `'handleClick'`, equivalent to `[$this, 'handleClick']`
+ * - object method: `[$object, 'handleClick']`
+ * - static method: `['Page', 'handleClick']`
* - anonymous function: `function($event) { ... }`
*
* The following is an example:
*
* ~~~
- * array(
+ * [
* 'beforeValidate' => 'myBeforeValidate',
* 'afterValidate' => 'myAfterValidate',
- * )
+ * ]
* ~~~
*
* @return array events (array keys) and the corresponding event handler methods (array values).
*/
public function events()
{
- return array();
+ return [];
}
/**
@@ -69,7 +69,7 @@ class Behavior extends \yii\base\Object
{
$this->owner = $owner;
foreach ($this->events() as $event => $handler) {
- $owner->on($event, is_string($handler) ? array($this, $handler) : $handler);
+ $owner->on($event, is_string($handler) ? [$this, $handler] : $handler);
}
}
@@ -83,7 +83,7 @@ class Behavior extends \yii\base\Object
{
if ($this->owner) {
foreach ($this->events() as $event => $handler) {
- $this->owner->off($event, is_string($handler) ? array($this, $handler) : $handler);
+ $this->owner->off($event, is_string($handler) ? [$this, $handler] : $handler);
}
$this->owner = null;
}
diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php
index 8e75835..4cea9d2 100644
--- a/framework/yii/base/Component.php
+++ b/framework/yii/base/Component.php
@@ -10,7 +10,12 @@ namespace yii\base;
use Yii;
/**
+ * Component is the base class that implements the *property*, *event* and *behavior* features.
+ *
* @include @yii/base/Component.md
+ *
+ * @property Behavior[] $behaviors List of behaviors attached to this component. This property is read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -35,10 +40,10 @@ class Component extends Object
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when executing `$value = $component->property;`.
* @param string $name the property name
- * @return mixed the property value, event handlers attached to the event,
- * the behavior, or the value of a behavior's property
+ * @return mixed the property value or the value of a behavior's property
* @throws UnknownPropertyException if the property is not defined
- * @see __set
+ * @throws InvalidCallException if the property is write-only.
+ * @see __set()
*/
public function __get($name)
{
@@ -55,7 +60,11 @@ class Component extends Object
}
}
}
- throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
+ if (method_exists($this, 'set' . $name)) {
+ throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name);
+ } else {
+ throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
+ }
}
/**
@@ -73,7 +82,7 @@ class Component extends Object
* @param mixed $value the property value
* @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is read-only.
- * @see __get
+ * @see __get()
*/
public function __set($name, $value)
{
@@ -172,9 +181,8 @@ class Component extends Object
/**
* Calls the named method which is not a class method.
- * If the name refers to a component property whose value is
- * an anonymous function, the method will execute the function.
- * Otherwise, it will check if any attached behavior has
+ *
+ * This method will check if any attached behavior has
* the named method and will execute it if available.
*
* Do not call this method directly as it is a PHP magic method that
@@ -186,18 +194,10 @@ class Component extends Object
*/
public function __call($name, $params)
{
- $getter = 'get' . $name;
- if (method_exists($this, $getter)) {
- $func = $this->$getter();
- if ($func instanceof \Closure) {
- return call_user_func_array($func, $params);
- }
- }
-
$this->ensureBehaviors();
foreach ($this->_behaviors as $object) {
- if (method_exists($object, $name)) {
- return call_user_func_array(array($object, $name), $params);
+ if ($object->hasMethod($name)) {
+ return call_user_func_array([$object, $name], $params);
}
}
@@ -220,19 +220,19 @@ class Component extends Object
*
* - the class has a getter or setter method associated with the specified name
* (in this case, property name is case-insensitive);
- * - the class has a member variable with the specified name (when `$checkVar` is true);
- * - an attached behavior has a property of the given name (when `$checkBehavior` is true).
+ * - the class has a member variable with the specified name (when `$checkVars` is true);
+ * - an attached behavior has a property of the given name (when `$checkBehaviors` is true).
*
* @param string $name the property name
- * @param boolean $checkVar whether to treat member variables as properties
- * @param boolean $checkBehavior whether to treat behaviors' properties as properties of this component
+ * @param boolean $checkVars whether to treat member variables as properties
+ * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
* @return boolean whether the property is defined
- * @see canGetProperty
- * @see canSetProperty
+ * @see canGetProperty()
+ * @see canSetProperty()
*/
- public function hasProperty($name, $checkVar = true, $checkBehavior = true)
+ public function hasProperty($name, $checkVars = true, $checkBehaviors = true)
{
- return $this->canGetProperty($name, $checkVar, $checkBehavior) || $this->canSetProperty($name, $checkVar, $checkBehavior);
+ return $this->canGetProperty($name, $checkVars, $checkBehaviors) || $this->canSetProperty($name, false, $checkBehaviors);
}
/**
@@ -241,28 +241,28 @@ class Component extends Object
*
* - the class has a getter method associated with the specified name
* (in this case, property name is case-insensitive);
- * - the class has a member variable with the specified name (when `$checkVar` is true);
- * - an attached behavior has a readable property of the given name (when `$checkBehavior` is true).
+ * - the class has a member variable with the specified name (when `$checkVars` is true);
+ * - an attached behavior has a readable property of the given name (when `$checkBehaviors` is true).
*
* @param string $name the property name
- * @param boolean $checkVar whether to treat member variables as properties
- * @param boolean $checkBehavior whether to treat behaviors' properties as properties of this component
+ * @param boolean $checkVars whether to treat member variables as properties
+ * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
* @return boolean whether the property can be read
- * @see canSetProperty
+ * @see canSetProperty()
*/
- public function canGetProperty($name, $checkVar = true, $checkBehavior = true)
+ public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
{
- if (method_exists($this, 'get' . $name) || $checkVar && property_exists($this, $name)) {
+ if (method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name)) {
return true;
- } else {
+ } elseif ($checkBehaviors) {
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
- if ($behavior->canGetProperty($name, $checkVar)) {
+ if ($behavior->canGetProperty($name, $checkVars)) {
return true;
}
}
- return false;
}
+ return false;
}
/**
@@ -271,28 +271,54 @@ class Component extends Object
*
* - the class has a setter method associated with the specified name
* (in this case, property name is case-insensitive);
- * - the class has a member variable with the specified name (when `$checkVar` is true);
- * - an attached behavior has a writable property of the given name (when `$checkBehavior` is true).
+ * - the class has a member variable with the specified name (when `$checkVars` is true);
+ * - an attached behavior has a writable property of the given name (when `$checkBehaviors` is true).
*
* @param string $name the property name
- * @param boolean $checkVar whether to treat member variables as properties
- * @param boolean $checkBehavior whether to treat behaviors' properties as properties of this component
+ * @param boolean $checkVars whether to treat member variables as properties
+ * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
* @return boolean whether the property can be written
- * @see canGetProperty
+ * @see canGetProperty()
*/
- public function canSetProperty($name, $checkVar = true, $checkBehavior = true)
+ public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
{
- if (method_exists($this, 'set' . $name) || $checkVar && property_exists($this, $name)) {
+ if (method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name)) {
return true;
- } else {
+ } elseif ($checkBehaviors) {
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
- if ($behavior->canSetProperty($name, $checkVar)) {
+ if ($behavior->canSetProperty($name, $checkVars)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns a value indicating whether a method is defined.
+ * A method is defined if:
+ *
+ * - the class has a method with the specified name
+ * - an attached behavior has a method with the given name (when `$checkBehaviors` is true).
+ *
+ * @param string $name the property name
+ * @param boolean $checkBehaviors whether to treat behaviors' methods as methods of this component
+ * @return boolean whether the property is defined
+ */
+ public function hasMethod($name, $checkBehaviors = true)
+ {
+ if (method_exists($this, $name)) {
+ return true;
+ } elseif ($checkBehaviors) {
+ $this->ensureBehaviors();
+ foreach ($this->_behaviors as $behavior) {
+ if ($behavior->hasMethod($name)) {
return true;
}
}
- return false;
}
+ return false;
}
/**
@@ -305,11 +331,11 @@ class Component extends Object
* the behavior class or an array of the following structure:
*
* ~~~
- * 'behaviorName' => array(
+ * 'behaviorName' => [
* 'class' => 'BehaviorClass',
* 'property1' => 'value1',
* 'property2' => 'value2',
- * )
+ * ]
* ~~~
*
* Note that a behavior class must extend from [[Behavior]]. Behavior names can be strings
@@ -323,7 +349,7 @@ class Component extends Object
*/
public function behaviors()
{
- return array();
+ return [];
}
/**
@@ -334,23 +360,23 @@ class Component extends Object
public function hasEventHandlers($name)
{
$this->ensureBehaviors();
- return !empty($this->_events[$name]);
+ return !empty($this->_events[$name]) || Event::hasHandlers($this, $name);
}
/**
* Attaches an event handler to an event.
*
- * An event handler must be a valid PHP callback. The followings are
+ * The event handler must be a valid PHP callback. The followings are
* some examples:
*
* ~~~
* function ($event) { ... } // anonymous function
- * array($object, 'handleClick') // $object->handleClick()
- * array('Page', 'handleClick') // Page::handleClick()
- * 'handleClick' // global function handleClick()
+ * [$object, 'handleClick'] // $object->handleClick()
+ * ['Page', 'handleClick'] // Page::handleClick()
+ * 'handleClick' // global function handleClick()
* ~~~
*
- * An event handler must be defined with the following signature,
+ * The event handler must be defined with the following signature,
*
* ~~~
* function ($event)
@@ -367,7 +393,7 @@ class Component extends Object
public function on($name, $handler, $data = null)
{
$this->ensureBehaviors();
- $this->_events[$name][] = array($handler, $data);
+ $this->_events[$name][] = [$handler, $data];
}
/**
@@ -382,34 +408,35 @@ class Component extends Object
public function off($name, $handler = null)
{
$this->ensureBehaviors();
- if (isset($this->_events[$name])) {
- if ($handler === null) {
- $this->_events[$name] = array();
- } else {
- $removed = false;
- foreach ($this->_events[$name] as $i => $event) {
- if ($event[0] === $handler) {
- unset($this->_events[$name][$i]);
- $removed = true;
- }
- }
- if ($removed) {
- $this->_events[$name] = array_values($this->_events[$name]);
+ if (empty($this->_events[$name])) {
+ return false;
+ }
+ if ($handler === null) {
+ unset($this->_events[$name]);
+ return true;
+ } else {
+ $removed = false;
+ foreach ($this->_events[$name] as $i => $event) {
+ if ($event[0] === $handler) {
+ unset($this->_events[$name][$i]);
+ $removed = true;
}
- return $removed;
}
+ if ($removed) {
+ $this->_events[$name] = array_values($this->_events[$name]);
+ }
+ return $removed;
}
- return false;
}
/**
* Triggers an event.
* This method represents the happening of an event. It invokes
- * all attached handlers for the event.
+ * all attached handlers for the event including class-level handlers.
* @param string $name the event name
* @param Event $event the event parameter. If not set, a default [[Event]] object will be created.
*/
- public function trigger($name, $event = null)
+ public function trigger($name, Event $event = null)
{
$this->ensureBehaviors();
if (!empty($this->_events[$name])) {
@@ -425,11 +452,13 @@ class Component extends Object
$event->data = $handler[1];
call_user_func($handler[0], $event);
// stop further handling if the event is handled
- if ($event instanceof Event && $event->handled) {
+ if ($event->handled) {
return;
}
}
}
+ // invoke class-level attached handlers
+ Event::trigger($this, $name, $event);
}
/**
@@ -466,7 +495,7 @@ class Component extends Object
* - an object configuration array that will be passed to [[Yii::createObject()]] to create the behavior object.
*
* @return Behavior the behavior object
- * @see detachBehavior
+ * @see detachBehavior()
*/
public function attachBehavior($name, $behavior)
{
@@ -479,7 +508,7 @@ class Component extends Object
* Each behavior is indexed by its name and should be a [[Behavior]] object,
* a string specifying the behavior class, or an configuration array for creating the behavior.
* @param array $behaviors list of behaviors to be attached to the component
- * @see attachBehavior
+ * @see attachBehavior()
*/
public function attachBehaviors($behaviors)
{
@@ -519,7 +548,7 @@ class Component extends Object
$this->detachBehavior($name);
}
}
- $this->_behaviors = array();
+ $this->_behaviors = [];
}
/**
@@ -528,7 +557,7 @@ class Component extends Object
public function ensureBehaviors()
{
if ($this->_behaviors === null) {
- $this->_behaviors = array();
+ $this->_behaviors = [];
foreach ($this->behaviors() as $name => $behavior) {
$this->attachBehaviorInternal($name, $behavior);
}
diff --git a/framework/yii/base/Controller.php b/framework/yii/base/Controller.php
index af33b63..d14c9dd 100644
--- a/framework/yii/base/Controller.php
+++ b/framework/yii/base/Controller.php
@@ -12,10 +12,20 @@ use Yii;
/**
* Controller is the base class for classes containing controller logic.
*
+ * @property array $actionParams The request parameters (name-value pairs) to be used for action parameter
+ * binding. This property is read-only.
+ * @property string $route The route (module ID, controller ID and action ID) of the current request. This
+ * property is read-only.
+ * @property string $uniqueId The controller ID that is prefixed with the module ID (if any). This property is
+ * read-only.
+ * @property View $view The view object that can be used to render views or view files.
+ * @property string $viewPath The directory containing the view files for this controller. This property is
+ * read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
-class Controller extends Component
+class Controller extends Component implements ViewContextInterface
{
/**
* @event ActionEvent an event raised right before executing a controller action.
@@ -26,7 +36,6 @@ class Controller extends Component
* @event ActionEvent an event raised right after executing a controller action.
*/
const EVENT_AFTER_ACTION = 'afterAction';
-
/**
* @var string the ID of this controller
*/
@@ -63,7 +72,7 @@ class Controller extends Component
* @param Module $module the module that this controller belongs to.
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($id, $module, $config = array())
+ public function __construct($id, $module, $config = [])
{
$this->id = $id;
$this->module = $module;
@@ -77,14 +86,14 @@ class Controller extends Component
* action class names or action configuration arrays. For example,
*
* ~~~
- * return array(
- * 'action1' => '@app/components/Action1',
- * 'action2' => array(
- * 'class' => '@app/components/Action2',
+ * return [
+ * 'action1' => 'app\components\Action1',
+ * 'action2' => [
+ * 'class' => 'app\components\Action2',
* 'property1' => 'value1',
* 'property2' => 'value2',
- * ),
- * );
+ * ],
+ * ];
* ~~~
*
* [[\Yii::createObject()]] will be used later to create the requested action
@@ -92,34 +101,41 @@ class Controller extends Component
*/
public function actions()
{
- return array();
+ return [];
}
/**
- * Runs an action with the specified action ID and parameters.
+ * Runs an action within this controller with the specified action ID and parameters.
* If the action ID is empty, the method will use [[defaultAction]].
* @param string $id the ID of the action to be executed.
* @param array $params the parameters (name-value pairs) to be passed to the action.
- * @return integer the status of the action execution. 0 means normal, other values mean abnormal.
+ * @return mixed the result of the action
* @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
- * @see createAction
+ * @see createAction()
*/
- public function runAction($id, $params = array())
+ public function runAction($id, $params = [])
{
$action = $this->createAction($id);
if ($action !== null) {
+ Yii::trace("Route to run: " . $action->getUniqueId(), __METHOD__);
+ if (Yii::$app->requestedAction === null) {
+ Yii::$app->requestedAction = $action;
+ }
$oldAction = $this->action;
$this->action = $action;
- $status = 1;
- if ($this->module->beforeAction($action)) {
- if ($this->beforeAction($action)) {
- $status = $action->runWithParams($params);
- $this->afterAction($action);
- }
- $this->module->afterAction($action);
+ $result = null;
+ $event = new ActionEvent($action);
+ Yii::$app->trigger(Application::EVENT_BEFORE_ACTION, $event);
+ if ($event->isValid && $this->module->beforeAction($action) && $this->beforeAction($action)) {
+ $result = $action->runWithParams($params);
+ $this->afterAction($action, $result);
+ $this->module->afterAction($action, $result);
+ $event = new ActionEvent($action);
+ $event->result = &$result;
+ Yii::$app->trigger(Application::EVENT_AFTER_ACTION, $event);
}
$this->action = $oldAction;
- return $status;
+ return $result;
} else {
throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);
}
@@ -132,11 +148,10 @@ class Controller extends Component
* the route will start from the application; otherwise, it will start from the parent module of this controller.
* @param string $route the route to be handled, e.g., 'view', 'comment/view', '/admin/comment/view'.
* @param array $params the parameters to be passed to the action.
- * @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal.
- * @see runAction
- * @see forward
+ * @return mixed the result of the action
+ * @see runAction()
*/
- public function run($route, $params = array())
+ public function run($route, $params = [])
{
$pos = strpos($route, '/');
if ($pos === false) {
@@ -157,22 +172,7 @@ class Controller extends Component
*/
public function bindActionParams($action, $params)
{
- return array();
- }
-
- /**
- * Forwards the current execution flow to handle a new request specified by a route.
- * The only difference between this method and [[run()]] is that after calling this method,
- * the application will exit.
- * @param string $route the route to be handled, e.g., 'view', 'comment/view', '/admin/comment/view'.
- * @param array $params the parameters to be passed to the action.
- * @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal.
- * @see run
- */
- public function forward($route, $params = array())
- {
- $status = $this->run($route, $params);
- Yii::$app->end($status);
+ return [];
}
/**
@@ -194,7 +194,7 @@ class Controller extends Component
$actionMap = $this->actions();
if (isset($actionMap[$id])) {
return Yii::createObject($actionMap[$id], $id, $this);
- } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) {
+ } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id) {
$methodName = 'action' . str_replace(' ', '', ucwords(implode(' ', explode('-', $id))));
if (method_exists($this, $methodName)) {
$method = new \ReflectionMethod($this, $methodName);
@@ -209,6 +209,7 @@ class Controller extends Component
/**
* This method is invoked right before an action is to be executed (after all possible filters.)
* You may override this method to do last-minute preparation for the action.
+ * If you override this method, please make sure you call the parent implementation first.
* @param Action $action the action to be executed.
* @return boolean whether the action should continue to be executed.
*/
@@ -222,11 +223,15 @@ class Controller extends Component
/**
* This method is invoked right after an action is executed.
* You may override this method to do some postprocessing for the action.
+ * If you override this method, please make sure you call the parent implementation first.
* @param Action $action the action just executed.
+ * @param mixed $result the action return result.
*/
- public function afterAction($action)
+ public function afterAction($action, &$result)
{
- $this->trigger(self::EVENT_AFTER_ACTION, new ActionEvent($action));
+ $event = new ActionEvent($action);
+ $event->result = & $result;
+ $this->trigger(self::EVENT_AFTER_ACTION, $event);
}
/**
@@ -238,7 +243,7 @@ class Controller extends Component
*/
public function getActionParams()
{
- return array();
+ return [];
}
/**
@@ -259,34 +264,6 @@ class Controller extends Component
}
/**
- * Populates one or multiple models from the given data array.
- * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array.
- * @param Model $model the model to be populated. If there are more than one model to be populated,
- * you may supply them as additional parameters.
- * @return boolean whether at least one model is successfully populated with the data.
- */
- public function populate($data, $model)
- {
- $success = false;
- if (!empty($data) && is_array($data)) {
- $models = func_get_args();
- array_shift($models);
- foreach ($models as $model) {
- /** @var Model $model */
- $scope = $model->formName();
- if ($scope == '') {
- $model->setAttributes($data);
- $success = true;
- } elseif (isset($data[$scope])) {
- $model->setAttributes($data[$scope]);
- $success = true;
- }
- }
- }
- return $success;
- }
-
- /**
* Renders a view and applies layout if available.
*
* The view to be rendered can be specified in one of the following formats:
@@ -315,7 +292,7 @@ class Controller extends Component
* - an absolute path (e.g. "/main"): the layout name starts with a slash. The actual layout file will be
* looked for under the [[Application::layoutPath|layout path]] of the application;
* - a relative path (e.g. "main"): the actual layout layout file will be looked for under the
- * [[Module::viewPath|view path]] of the context module.
+ * [[Module::layoutPath|layout path]] of the context module.
*
* If the layout name does not contain a file extension, it will use the default one `.php`.
*
@@ -325,13 +302,12 @@ class Controller extends Component
* @return string the rendering result.
* @throws InvalidParamException if the view file or the layout file does not exist.
*/
- public function render($view, $params = array())
+ public function render($view, $params = [])
{
- $viewFile = $this->findViewFile($view);
- $output = $this->getView()->renderFile($viewFile, $params, $this);
- $layoutFile = $this->findLayoutFile();
+ $output = $this->getView()->render($view, $params, $this);
+ $layoutFile = $this->findLayoutFile($this->getView());
if ($layoutFile !== false) {
- return $this->getView()->renderFile($layoutFile, array('content' => $output), $this);
+ return $this->getView()->renderFile($layoutFile, ['content' => $output], $this);
} else {
return $output;
}
@@ -345,10 +321,9 @@ class Controller extends Component
* @return string the rendering result.
* @throws InvalidParamException if the view file does not exist.
*/
- public function renderPartial($view, $params = array())
+ public function renderPartial($view, $params = [])
{
- $viewFile = $this->findViewFile($view);
- return $this->getView()->renderFile($viewFile, $params, $this);
+ return $this->getView()->render($view, $params, $this);
}
/**
@@ -358,7 +333,7 @@ class Controller extends Component
* @return string the rendering result.
* @throws InvalidParamException if the view file does not exist.
*/
- public function renderFile($file, $params = array())
+ public function renderFile($file, $params = [])
{
return $this->getView()->renderFile($file, $params, $this);
}
@@ -404,59 +379,51 @@ class Controller extends Component
* on how to specify this parameter.
* @return string the view file path. Note that the file may not exist.
*/
- protected function findViewFile($view)
+ public function findViewFile($view)
{
- if (strncmp($view, '@', 1) === 0) {
- // e.g. "@app/views/main"
- $file = Yii::getAlias($view);
- } elseif (strncmp($view, '//', 2) === 0) {
- // e.g. "//layouts/main"
- $file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
- } elseif (strncmp($view, '/', 1) === 0) {
- // e.g. "/site/index"
- $file = $this->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
- } else {
- $file = $this->getViewPath() . DIRECTORY_SEPARATOR . $view;
- }
-
- return pathinfo($file, PATHINFO_EXTENSION) === '' ? $file . '.php' : $file;
+ return $this->getViewPath() . DIRECTORY_SEPARATOR . $view;
}
/**
* Finds the applicable layout file.
+ * @param View $view the view object to render the layout file
* @return string|boolean the layout file path, or false if layout is not needed.
* Please refer to [[render()]] on how to specify this parameter.
* @throws InvalidParamException if an invalid path alias is used to specify the layout
*/
- protected function findLayoutFile()
+ protected function findLayoutFile($view)
{
$module = $this->module;
if (is_string($this->layout)) {
- $view = $this->layout;
+ $layout = $this->layout;
} elseif ($this->layout === null) {
while ($module !== null && $module->layout === null) {
$module = $module->module;
}
if ($module !== null && is_string($module->layout)) {
- $view = $module->layout;
+ $layout = $module->layout;
}
}
- if (!isset($view)) {
+ if (!isset($layout)) {
return false;
}
- if (strncmp($view, '@', 1) === 0) {
- $file = Yii::getAlias($view);
- } elseif (strncmp($view, '/', 1) === 0) {
- $file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
+ if (strncmp($layout, '@', 1) === 0) {
+ $file = Yii::getAlias($layout);
+ } elseif (strncmp($layout, '/', 1) === 0) {
+ $file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . $layout;
} else {
- $file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
+ $file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $layout;
}
- if (pathinfo($file, PATHINFO_EXTENSION) === '') {
- $file .= '.php';
+ if (pathinfo($file, PATHINFO_EXTENSION) !== '') {
+ return $file;
+ }
+ $path = $file . '.' . $view->defaultExtension;
+ if ($view->defaultExtension !== 'php' && !is_file($path)) {
+ $path = $file . '.php';
}
- return $file;
+ return $path;
}
}
diff --git a/framework/yii/base/ErrorException.php b/framework/yii/base/ErrorException.php
index 8e1977a..7c6ff6f 100644
--- a/framework/yii/base/ErrorException.php
+++ b/framework/yii/base/ErrorException.php
@@ -20,7 +20,7 @@ class ErrorException extends Exception
protected $severity;
/**
- * Constructs the exception
+ * Constructs the exception.
* @link http://php.net/manual/en/errorexception.construct.php
* @param $message [optional]
* @param $code [optional]
@@ -51,7 +51,6 @@ class ErrorException extends Exception
}
// XDebug has a different key name
- $frame['args'] = array();
if (isset($frame['params']) && !isset($frame['args'])) {
$frame['args'] = $frame['params'];
}
@@ -64,24 +63,24 @@ class ErrorException extends Exception
}
/**
- * Gets the exception severity
- * @link http://php.net/manual/en/errorexception.getseverity.php
- * @return int the severity level of the exception.
+ * Returns if error is one of fatal type.
+ *
+ * @param array $error error got from error_get_last()
+ * @return bool if error is one of fatal type
*/
- final public function getSeverity()
+ public static function isFatalError($error)
{
- return $this->severity;
+ return isset($error['type']) && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING]);
}
/**
- * Returns if error is one of fatal type
- *
- * @param array $error error got from error_get_last()
- * @return bool if error is one of fatal type
+ * Gets the exception severity.
+ * @link http://php.net/manual/en/errorexception.getseverity.php
+ * @return int the severity level of the exception.
*/
- public static function isFatalError($error)
+ final public function getSeverity()
{
- return isset($error['type']) && in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING));
+ return $this->severity;
}
/**
@@ -89,7 +88,7 @@ class ErrorException extends Exception
*/
public function getName()
{
- $names = array(
+ $names = [
E_ERROR => Yii::t('yii', 'Fatal Error'),
E_PARSE => Yii::t('yii', 'Parse Error'),
E_CORE_ERROR => Yii::t('yii', 'Core Error'),
@@ -103,7 +102,7 @@ class ErrorException extends Exception
E_NOTICE => Yii::t('yii', 'Notice'),
E_RECOVERABLE_ERROR => Yii::t('yii', 'Recoverable Error'),
E_DEPRECATED => Yii::t('yii', 'Deprecated'),
- );
+ ];
return isset($names[$this->getCode()]) ? $names[$this->getCode()] : Yii::t('yii', 'Error');
}
}
diff --git a/framework/yii/base/ErrorHandler.php b/framework/yii/base/ErrorHandler.php
index 4e3e92a..1014f71 100644
--- a/framework/yii/base/ErrorHandler.php
+++ b/framework/yii/base/ErrorHandler.php
@@ -8,6 +8,7 @@
namespace yii\base;
use Yii;
+use yii\web\HttpException;
/**
* ErrorHandler handles uncaught PHP errors and exceptions.
@@ -15,6 +16,9 @@ use Yii;
* ErrorHandler displays these errors using appropriate views based on the
* nature of the errors and the mode the application runs at.
*
+ * ErrorHandler is configured as an application component in [[yii\base\Application]] by default.
+ * You can access that instance via `Yii::$app->errorHandler`.
+ *
* @author Qiang Xue
* @author Timur Ruziev
* @since 2.0
@@ -36,14 +40,18 @@ class ErrorHandler extends Component
/**
* @var string the route (e.g. 'site/error') to the controller action that will be used
* to display external errors. Inside the action, it can retrieve the error information
- * by Yii::$app->errorHandler->error. This property defaults to null, meaning ErrorHandler
+ * by Yii::$app->exception. This property defaults to null, meaning ErrorHandler
* will handle the error display.
*/
public $errorAction;
/**
- * @var string the path of the view file for rendering exceptions and errors.
+ * @var string the path of the view file for rendering exceptions without call stack information.
+ */
+ public $errorView = '@yii/views/errorHandler/error.php';
+ /**
+ * @var string the path of the view file for rendering exceptions.
*/
- public $mainView = '@yii/views/errorHandler/main.php';
+ public $exceptionView = '@yii/views/errorHandler/exception.php';
/**
* @var string the path of the view file for rendering exceptions and errors call stack element.
*/
@@ -72,47 +80,61 @@ class ErrorHandler extends Component
}
/**
- * Renders exception.
- * @param \Exception $exception to be handled.
+ * Renders the exception.
+ * @param \Exception $exception the exception to be handled.
*/
protected function renderException($exception)
{
- if ($this->errorAction !== null) {
- Yii::$app->runAction($this->errorAction);
- } elseif (!(Yii::$app instanceof \yii\web\Application)) {
- Yii::$app->renderException($exception);
- } else {
- $response = Yii::$app->getResponse();
- if (!headers_sent()) {
- if ($exception instanceof HttpException) {
- $response->setStatusCode($exception->statusCode);
- } else {
- $response->setStatusCode(500);
- }
+ if (Yii::$app instanceof \yii\console\Application || YII_ENV_TEST) {
+ echo Yii::$app->renderException($exception);
+ return;
+ }
+
+ $useErrorView = !YII_DEBUG || $exception instanceof UserException;
+
+ $response = Yii::$app->getResponse();
+ $response->getHeaders()->removeAll();
+
+ if ($useErrorView && $this->errorAction !== null) {
+ $result = Yii::$app->runAction($this->errorAction);
+ if ($result instanceof Response) {
+ $response = $result;
+ } else {
+ $response->data = $result;
}
+ } elseif ($response->format === \yii\web\Response::FORMAT_HTML) {
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
- Yii::$app->renderException($exception);
+ // AJAX request
+ $response->data = Yii::$app->renderException($exception);
} else {
// if there is an error during error rendering it's useful to
// display PHP error in debug mode instead of a blank screen
if (YII_DEBUG) {
ini_set('display_errors', 1);
}
-
- $view = new View();
- $request = '';
- foreach (array('_GET', '_POST', '_SERVER', '_FILES', '_COOKIE', '_SESSION', '_ENV') as $name) {
- if (!empty($GLOBALS[$name])) {
- $request .= '$' . $name . ' = ' . var_export($GLOBALS[$name], true) . ";\n\n";
- }
- }
- $request = rtrim($request, "\n\n");
- $response->content = $view->renderFile($this->mainView, array(
+ $file = $useErrorView ? $this->errorView : $this->exceptionView;
+ $response->data = $this->renderFile($file, [
'exception' => $exception,
- 'request' => $request,
- ), $this);
+ ]);
}
+ } elseif ($exception instanceof Arrayable) {
+ $response->data = $exception;
+ } else {
+ $response->data = [
+ 'type' => get_class($exception),
+ 'name' => 'Exception',
+ 'message' => $exception->getMessage(),
+ 'code' => $exception->getCode(),
+ ];
+ }
+
+ if ($exception instanceof HttpException) {
+ $response->setStatusCode($exception->statusCode);
+ } else {
+ $response->setStatusCode(500);
}
+
+ $response->send();
}
/**
@@ -132,7 +154,9 @@ class ErrorHandler extends Component
{
// the following manual level counting is to deal with zlib.output_compression set to On
for ($level = ob_get_level(); $level > 0; --$level) {
- @ob_end_clean();
+ if (!@ob_end_clean()) {
+ ob_clean();
+ }
}
}
@@ -152,24 +176,32 @@ class ErrorHandler extends Component
$html = rtrim($html, '\\');
} elseif (strpos($code, '()') !== false) {
// method/function call
- $self = $this;
- $html = preg_replace_callback('/^(.*)\(\)$/', function ($matches) use ($self) {
- return '' .
- $self->htmlEncode($matches[1]) . ' ()';
+ $html = preg_replace_callback('/^(.*)\(\)$/', function ($matches) {
+ return '' .
+ $this->htmlEncode($matches[1]) . ' ()';
}, $code);
}
return $html;
}
/**
- * Creates HTML containing link to the page with the information on given HTTP status code.
- * @param integer $statusCode to be used to generate information link.
- * @param string $statusDescription Description to display after the the status code.
- * @return string generated HTML with HTTP status code information.
+ * Renders a view file as a PHP script.
+ * @param string $_file_ the view file.
+ * @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.
+ * @return string the rendering result
*/
- public function createHttpStatusLink($statusCode, $statusDescription)
+ public function renderFile($_file_, $_params_)
{
- return 'HTTP ' . (int)$statusCode . ' – ' . $statusDescription . ' ';
+ $_params_['handler'] = $this;
+ if ($this->exception instanceof ErrorException) {
+ ob_start();
+ ob_implicit_flush(false);
+ extract($_params_, EXTR_OVERWRITE);
+ require(Yii::getAlias($_file_));
+ return ob_get_clean();
+ } else {
+ return Yii::$app->getView()->renderFile($_file_, $_params_, $this);
+ }
}
/**
@@ -180,14 +212,11 @@ class ErrorHandler extends Component
*/
public function renderPreviousExceptions($exception)
{
- if (($previous = $exception->getPrevious()) === null) {
+ if (($previous = $exception->getPrevious()) !== null) {
+ return $this->renderFile($this->previousExceptionView, ['exception' => $previous]);
+ } else {
return '';
}
- $view = new View();
- return $view->renderFile($this->previousExceptionView, array(
- 'exception' => $previous,
- 'previousHtml' => $this->renderPreviousExceptions($previous),
- ), $this);
}
/**
@@ -201,7 +230,7 @@ class ErrorHandler extends Component
*/
public function renderCallStackItem($file, $line, $class, $method, $index)
{
- $lines = array();
+ $lines = [];
$begin = $end = 0;
if ($file !== null && $line !== null) {
$line--; // adjust line number from one-based to zero-based
@@ -215,8 +244,7 @@ class ErrorHandler extends Component
$end = $line + $half < $lineCount ? $line + $half : $lineCount - 1;
}
- $view = new View();
- return $view->renderFile($this->callStackItemView, array(
+ return $this->renderFile($this->callStackItemView, [
'file' => $file,
'line' => $line,
'class' => $class,
@@ -225,7 +253,22 @@ class ErrorHandler extends Component
'lines' => $lines,
'begin' => $begin,
'end' => $end,
- ), $this);
+ ]);
+ }
+
+ /**
+ * Renders the request information.
+ * @return string the rendering result
+ */
+ public function renderRequest()
+ {
+ $request = '';
+ foreach (['_GET', '_POST', '_SERVER', '_FILES', '_COOKIE', '_SESSION', '_ENV'] as $name) {
+ if (!empty($GLOBALS[$name])) {
+ $request .= '$' . $name . ' = ' . var_export($GLOBALS[$name], true) . ";\n\n";
+ }
+ }
+ return '' . rtrim($request, "\n") . ' ';
}
/**
@@ -239,20 +282,31 @@ class ErrorHandler extends Component
}
/**
+ * Creates HTML containing link to the page with the information on given HTTP status code.
+ * @param integer $statusCode to be used to generate information link.
+ * @param string $statusDescription Description to display after the the status code.
+ * @return string generated HTML with HTTP status code information.
+ */
+ public function createHttpStatusLink($statusCode, $statusDescription)
+ {
+ return 'HTTP ' . (int)$statusCode . ' – ' . $statusDescription . ' ';
+ }
+
+ /**
* Creates string containing HTML link which refers to the home page of determined web-server software
* and its full name.
* @return string server software information hyperlink.
*/
public function createServerInformationLink()
{
- static $serverUrls = array(
- 'http://httpd.apache.org/' => array('apache'),
- 'http://nginx.org/' => array('nginx'),
- 'http://lighttpd.net/' => array('lighttpd'),
- 'http://gwan.com/' => array('g-wan', 'gwan'),
- 'http://iis.net/' => array('iis', 'services'),
- 'http://php.net/manual/en/features.commandline.webserver.php' => array('development'),
- );
+ static $serverUrls = [
+ 'http://httpd.apache.org/' => ['apache'],
+ 'http://nginx.org/' => ['nginx'],
+ 'http://lighttpd.net/' => ['lighttpd'],
+ 'http://gwan.com/' => ['g-wan', 'gwan'],
+ 'http://iis.net/' => ['iis', 'services'],
+ 'http://php.net/manual/en/features.commandline.webserver.php' => ['development'],
+ ];
if (isset($_SERVER['SERVER_SOFTWARE'])) {
foreach ($serverUrls as $url => $keywords) {
foreach ($keywords as $keyword) {
diff --git a/framework/yii/base/Event.php b/framework/yii/base/Event.php
index 5d40736..974a1a4 100644
--- a/framework/yii/base/Event.php
+++ b/framework/yii/base/Event.php
@@ -45,4 +45,139 @@ class Event extends Object
* Note that this varies according to which event handler is currently executing.
*/
public $data;
+
+ private static $_events = [];
+
+ /**
+ * Attaches an event handler to a class-level event.
+ *
+ * When a class-level event is triggered, event handlers attached
+ * to that class and all parent classes will be invoked.
+ *
+ * For example, the following code attaches an event handler to `ActiveRecord`'s
+ * `afterInsert` event:
+ *
+ * ~~~
+ * Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
+ * Yii::trace(get_class($event->sender) . ' is inserted.');
+ * });
+ * ~~~
+ *
+ * The handler will be invoked for EVERY successful ActiveRecord insertion.
+ *
+ * For more details about how to declare an event handler, please refer to [[Component::on()]].
+ *
+ * @param string $class the fully qualified class name to which the event handler needs to attach
+ * @param string $name the event name
+ * @param callback $handler the event handler
+ * @param mixed $data the data to be passed to the event handler when the event is triggered.
+ * When the event handler is invoked, this data can be accessed via [[Event::data]].
+ * @see off()
+ */
+ public static function on($class, $name, $handler, $data = null)
+ {
+ self::$_events[$name][ltrim($class, '\\')][] = [$handler, $data];
+ }
+
+ /**
+ * Detaches an event handler from a class-level event.
+ *
+ * This method is the opposite of [[on()]].
+ *
+ * @param string $class the fully qualified class name from which the event handler needs to be detached
+ * @param string $name the event name
+ * @param callback $handler the event handler to be removed.
+ * If it is null, all handlers attached to the named event will be removed.
+ * @return boolean if a handler is found and detached
+ * @see on()
+ */
+ public static function off($class, $name, $handler = null)
+ {
+ $class = ltrim($class, '\\');
+ if (empty(self::$_events[$name][$class])) {
+ return false;
+ }
+ if ($handler === null) {
+ unset(self::$_events[$name][$class]);
+ return true;
+ } else {
+ $removed = false;
+ foreach (self::$_events[$name][$class] as $i => $event) {
+ if ($event[0] === $handler) {
+ unset(self::$_events[$name][$class][$i]);
+ $removed = true;
+ }
+ }
+ if ($removed) {
+ self::$_events[$name][$class] = array_values(self::$_events[$name][$class]);
+ }
+ return $removed;
+ }
+ }
+
+ /**
+ * Returns a value indicating whether there is any handler attached to the specified class-level event.
+ * Note that this method will also check all parent classes to see if there is any handler attached
+ * to the named event.
+ * @param string|object $class the object or the fully qualified class name specifying the class-level event
+ * @param string $name the event name
+ * @return boolean whether there is any handler attached to the event.
+ */
+ public static function hasHandlers($class, $name)
+ {
+ if (empty(self::$_events[$name])) {
+ return false;
+ }
+ if (is_object($class)) {
+ $class = get_class($class);
+ } else {
+ $class = ltrim($class, '\\');
+ }
+ do {
+ if (!empty(self::$_events[$name][$class])) {
+ return true;
+ }
+ } while (($class = get_parent_class($class)) !== false);
+ return false;
+ }
+
+ /**
+ * Triggers a class-level event.
+ * This method will cause invocation of event handlers that are attached to the named event
+ * for the specified class and all its parent classes.
+ * @param string|object $class the object or the fully qualified class name specifying the class-level event
+ * @param string $name the event name
+ * @param Event $event the event parameter. If not set, a default [[Event]] object will be created.
+ */
+ public static function trigger($class, $name, $event = null)
+ {
+ if (empty(self::$_events[$name])) {
+ return;
+ }
+ if ($event === null) {
+ $event = new self;
+ }
+ $event->handled = false;
+ $event->name = $name;
+
+ if (is_object($class)) {
+ if ($event->sender === null) {
+ $event->sender = $class;
+ }
+ $class = get_class($class);
+ } else {
+ $class = ltrim($class, '\\');
+ }
+ do {
+ if (!empty(self::$_events[$name][$class])) {
+ foreach (self::$_events[$name][$class] as $handler) {
+ $event->data = $handler[1];
+ call_user_func($handler[0], $event);
+ if ($event instanceof Event && $event->handled) {
+ return;
+ }
+ }
+ }
+ } while (($class = get_parent_class($class)) !== false);
+ }
}
diff --git a/framework/yii/base/Exception.php b/framework/yii/base/Exception.php
index 956f17b..7e01bd4 100644
--- a/framework/yii/base/Exception.php
+++ b/framework/yii/base/Exception.php
@@ -13,7 +13,7 @@ namespace yii\base;
* @author Qiang Xue
* @since 2.0
*/
-class Exception extends \Exception
+class Exception extends \Exception implements Arrayable
{
/**
* @return string the user-friendly name of this exception
@@ -22,4 +22,32 @@ class Exception extends \Exception
{
return \Yii::t('yii', 'Exception');
}
+
+ /**
+ * Returns the array representation of this object.
+ * @return array the array representation of this object.
+ */
+ public function toArray()
+ {
+ return $this->toArrayRecursive($this);
+ }
+
+ /**
+ * Returns the array representation of the exception and all previous exceptions recursively.
+ * @param \Exception exception object
+ * @return array the array representation of the exception.
+ */
+ protected function toArrayRecursive($exception)
+ {
+ $array = [
+ 'type' => get_class($exception),
+ 'name' => $exception instanceof self ? $exception->getName() : 'Exception',
+ 'message' => $exception->getMessage(),
+ 'code' => $exception->getCode(),
+ ];
+ if (($prev = $exception->getPrevious()) !== null) {
+ $array['previous'] = $this->toArrayRecursive($prev);
+ }
+ return $array;
+ }
}
diff --git a/framework/yii/base/Extension.php b/framework/yii/base/Extension.php
new file mode 100644
index 0000000..c25a043
--- /dev/null
+++ b/framework/yii/base/Extension.php
@@ -0,0 +1,29 @@
+
+ * @since 2.0
+ */
+class Extension
+{
+ /**
+ * Initializes the extension.
+ * This method is invoked at the end of [[Application::init()]].
+ */
+ public static function init()
+ {
+ }
+}
diff --git a/framework/yii/base/Formatter.php b/framework/yii/base/Formatter.php
index 545f570..bfc1e36 100644
--- a/framework/yii/base/Formatter.php
+++ b/framework/yii/base/Formatter.php
@@ -19,6 +19,9 @@ use yii\helpers\Html;
* The behavior of some of them may be configured via the properties of Formatter. For example,
* by configuring [[dateFormat]], one may control how [[asDate()]] formats the value into a date string.
*
+ * Formatter is configured as an application component in [[yii\base\Application]] by default.
+ * You can access that instance via `Yii::$app->formatter`.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -37,8 +40,12 @@ class Formatter extends Component
*/
public $datetimeFormat = 'Y/m/d h:i:s A';
/**
+ * @var string the text to be displayed when formatting a null. Defaults to '(not set) '.
+ */
+ public $nullDisplay;
+ /**
* @var array the text to be displayed when formatting a boolean value. The first element corresponds
- * to the text display for false, the second element for true. Defaults to `array('No', 'Yes')`.
+ * to the text display for false, the second element for true. Defaults to `['No', 'Yes']`.
*/
public $booleanFormat;
/**
@@ -59,7 +66,44 @@ class Formatter extends Component
public function init()
{
if (empty($this->booleanFormat)) {
- $this->booleanFormat = array(Yii::t('yii', 'No'), Yii::t('yii', 'Yes'));
+ $this->booleanFormat = [Yii::t('yii', 'No'), Yii::t('yii', 'Yes')];
+ }
+ if ($this->nullDisplay === null) {
+ $this->nullDisplay = '' . Yii::t('yii', '(not set)') . ' ';
+ }
+ }
+
+ /**
+ * Formats the value based on the given format type.
+ * This method will call one of the "as" methods available in this class to do the formatting.
+ * For type "xyz", the method "asXyz" will be used. For example, if the format is "html",
+ * then [[asHtml()]] will be used. Format names are case insensitive.
+ * @param mixed $value the value to be formatted
+ * @param string|array $format the format of the value, e.g., "html", "text". To specify additional
+ * parameters of the formatting method, you may use an array. The first element of the array
+ * specifies the format name, while the rest of the elements will be used as the parameters to the formatting
+ * method. For example, a format of `['date', 'Y-m-d']` will cause the invocation of `asDate($value, 'Y-m-d')`.
+ * @return string the formatting result
+ * @throws InvalidParamException if the type is not supported by this class.
+ */
+ public function format($value, $format)
+ {
+ if (is_array($format)) {
+ if (!isset($format[0])) {
+ throw new InvalidParamException('The $format array must contain at least one element.');
+ }
+ $f = $format[0];
+ $format[0] = $value;
+ $params = $format;
+ $format = $f;
+ } else {
+ $params = [$value];
+ }
+ $method = 'as' . $format;
+ if (method_exists($this, $method)) {
+ return call_user_func_array([$this, $method], $params);
+ } else {
+ throw new InvalidParamException("Unknown type: $format");
}
}
@@ -71,6 +115,9 @@ class Formatter extends Component
*/
public function asRaw($value)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return $value;
}
@@ -81,6 +128,9 @@ class Formatter extends Component
*/
public function asText($value)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return Html::encode($value);
}
@@ -91,6 +141,9 @@ class Formatter extends Component
*/
public function asNtext($value)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return nl2br(Html::encode($value));
}
@@ -103,6 +156,9 @@ class Formatter extends Component
*/
public function asParagraphs($value)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return str_replace('
', '',
'' . preg_replace('/[\r\n]{2,}/', "
\n", Html::encode($value)) . '
'
);
@@ -118,6 +174,9 @@ class Formatter extends Component
*/
public function asHtml($value, $config = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return HtmlPurifier::process($value, $config);
}
@@ -128,7 +187,10 @@ class Formatter extends Component
*/
public function asEmail($value)
{
- return Html::mailto($value);
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
+ return Html::mailto(Html::encode($value), $value);
}
/**
@@ -138,6 +200,9 @@ class Formatter extends Component
*/
public function asImage($value)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return Html::img($value);
}
@@ -148,6 +213,9 @@ class Formatter extends Component
*/
public function asUrl($value)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
$url = $value;
if (strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0) {
$url = 'http://' . $url;
@@ -163,6 +231,9 @@ class Formatter extends Component
*/
public function asBoolean($value)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return $value ? $this->booleanFormat[1] : $this->booleanFormat[0];
}
@@ -183,6 +254,9 @@ class Formatter extends Component
*/
public function asDate($value, $format = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
$value = $this->normalizeDatetimeValue($value);
return date($format === null ? $this->dateFormat : $format, $value);
}
@@ -204,6 +278,9 @@ class Formatter extends Component
*/
public function asTime($value, $format = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
$value = $this->normalizeDatetimeValue($value);
return date($format === null ? $this->timeFormat : $format, $value);
}
@@ -225,6 +302,9 @@ class Formatter extends Component
*/
public function asDatetime($value, $format = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
$value = $this->normalizeDatetimeValue($value);
return date($format === null ? $this->datetimeFormat : $format, $value);
}
@@ -232,16 +312,12 @@ class Formatter extends Component
/**
* Normalizes the given datetime value as one that can be taken by various date/time formatting methods.
* @param mixed $value the datetime value to be normalized.
- * @return mixed the normalized datetime value
+ * @return integer the normalized datetime value
*/
protected function normalizeDatetimeValue($value)
{
if (is_string($value)) {
- if (ctype_digit($value) || $value[0] === '-' && ctype_digit(substr($value, 1))) {
- return (int)$value;
- } else {
- return strtotime($value);
- }
+ return is_numeric($value) || $value === '' ? (int)$value : strtotime($value);
} elseif ($value instanceof DateTime) {
return $value->getTimestamp();
} else {
@@ -256,6 +332,9 @@ class Formatter extends Component
*/
public function asInteger($value)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
if (is_string($value) && preg_match('/^(-?\d+)/', $value, $matches)) {
return $matches[1];
} else {
@@ -274,6 +353,9 @@ class Formatter extends Component
*/
public function asDouble($value, $decimals = 2)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
if ($this->decimalSeparator === null) {
return sprintf("%.{$decimals}f", $value);
} else {
@@ -292,6 +374,9 @@ class Formatter extends Component
*/
public function asNumber($value, $decimals = 0)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
$ds = isset($this->decimalSeparator) ? $this->decimalSeparator: '.';
$ts = isset($this->thousandSeparator) ? $this->thousandSeparator: ',';
return number_format($value, $decimals, $ds, $ts);
diff --git a/framework/yii/base/HttpException.php b/framework/yii/base/HttpException.php
deleted file mode 100644
index cce0bb0..0000000
--- a/framework/yii/base/HttpException.php
+++ /dev/null
@@ -1,52 +0,0 @@
-
- * @since 2.0
- */
-class HttpException extends UserException
-{
- /**
- * @var integer HTTP status code, such as 403, 404, 500, etc.
- */
- public $statusCode;
-
- /**
- * Constructor.
- * @param integer $status HTTP status code, such as 404, 500, etc.
- * @param string $message error message
- * @param integer $code error code
- * @param \Exception $previous The previous exception used for the exception chaining.
- */
- public function __construct($status, $message = null, $code = 0, \Exception $previous = null)
- {
- $this->statusCode = $status;
- parent::__construct($message, $code, $previous);
- }
-
- /**
- * @return string the user-friendly name of this exception
- */
- public function getName()
- {
- if (isset(\yii\web\Response::$statusTexts[$this->statusCode])) {
- return \yii\web\Response::$statusTexts[$this->statusCode];
- } else {
- return 'Error';
- }
- }
-}
diff --git a/framework/yii/base/InlineAction.php b/framework/yii/base/InlineAction.php
index c5afe28..412b357 100644
--- a/framework/yii/base/InlineAction.php
+++ b/framework/yii/base/InlineAction.php
@@ -7,6 +7,8 @@
namespace yii\base;
+use Yii;
+
/**
* InlineAction represents an action that is defined as a controller method.
*
@@ -29,7 +31,7 @@ class InlineAction extends Action
* @param string $actionMethod the controller method that this inline action is associated with
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($id, $controller, $actionMethod, $config = array())
+ public function __construct($id, $controller, $actionMethod, $config = [])
{
$this->actionMethod = $actionMethod;
parent::__construct($id, $controller, $config);
@@ -39,11 +41,15 @@ class InlineAction extends Action
* Runs this action with the specified parameters.
* This method is mainly invoked by the controller.
* @param array $params action parameters
- * @return integer the exit status (0 means normal, non-zero means abnormal).
+ * @return mixed the result of the action
*/
public function runWithParams($params)
{
$args = $this->controller->bindActionParams($this, $params);
- return (int)call_user_func_array(array($this->controller, $this->actionMethod), $args);
+ Yii::trace('Running action: ' . get_class($this->controller) . '::' . $this->actionMethod . '()', __METHOD__);
+ if (Yii::$app->requestedParams === null) {
+ Yii::$app->requestedParams = $args;
+ }
+ return call_user_func_array([$this->controller, $this->actionMethod], $args);
}
}
diff --git a/framework/yii/base/Jsonable.php b/framework/yii/base/Jsonable.php
deleted file mode 100644
index e9425a6..0000000
--- a/framework/yii/base/Jsonable.php
+++ /dev/null
@@ -1,22 +0,0 @@
-
- * @since 2.0
- */
-interface Jsonable
-{
- /**
- * @return string the JSON representation of this object
- */
- public function toJson();
-}
diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php
index b9b7d2d..ba51ec7 100644
--- a/framework/yii/base/Model.php
+++ b/framework/yii/base/Model.php
@@ -7,10 +7,13 @@
namespace yii\base;
+use Yii;
+use ArrayAccess;
use ArrayObject;
use ArrayIterator;
+use ReflectionClass;
+use IteratorAggregate;
use yii\helpers\Inflector;
-use yii\helpers\Json;
use yii\validators\RequiredValidator;
use yii\validators\Validator;
@@ -33,18 +36,30 @@ use yii\validators\Validator;
* You may directly use Model to store model data, or extend it with customization.
* You may also customize Model by attaching [[ModelBehavior|model behaviors]].
*
- * @property ArrayObject $validators All the validators declared in the model.
- * @property array $activeValidators The validators applicable to the current [[scenario]].
- * @property array $errors Errors for all attributes or the specified attribute. Empty array is returned if no error.
+ * @property \yii\validators\Validator[] $activeValidators The validators applicable to the current
+ * [[scenario]]. This property is read-only.
* @property array $attributes Attribute values (name => value).
- * @property string $scenario The scenario that this model is in.
+ * @property array $errors An array of errors for all attributes. Empty array is returned if no error. The
+ * result is a two-dimensional array. See [[getErrors()]] for detailed description. This property is read-only.
+ * @property array $firstErrors The first errors. An empty array will be returned if there is no error. This
+ * property is read-only.
+ * @property ArrayIterator $iterator An iterator for traversing the items in the list. This property is
+ * read-only.
+ * @property string $scenario The scenario that this model is in. Defaults to [[DEFAULT_SCENARIO]].
+ * @property ArrayObject|\yii\validators\Validator[] $validators All the validators declared in the model.
+ * This property is read-only.
*
* @author Qiang Xue
* @since 2.0
*/
-class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsonable
+class Model extends Component implements IteratorAggregate, ArrayAccess
{
/**
+ * The name of the default scenario.
+ */
+ const DEFAULT_SCENARIO = 'default';
+
+ /**
* @event ModelEvent an event raised at the beginning of [[validate()]]. You may set
* [[ModelEvent::isValid]] to be false to stop the validation.
*/
@@ -65,7 +80,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
/**
* @var string current scenario
*/
- private $_scenario = 'default';
+ private $_scenario = self::DEFAULT_SCENARIO;
/**
* Returns the validation rules for attributes.
@@ -76,20 +91,20 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* Each rule is an array with the following structure:
*
* ~~~
- * array(
- * 'attribute list',
+ * [
+ * ['attribute1', 'attribute2'],
* 'validator type',
- * 'on' => 'scenario name',
+ * 'on' => ['scenario1', 'scenario2'],
* ...other parameters...
- * )
+ * ]
* ~~~
*
* where
*
- * - attribute list: required, specifies the attributes (separated by commas) to be validated;
+ * - attribute list: required, specifies the attributes array to be validated, for single attribute you can pass string;
* - validator type: required, specifies the validator to be used. It can be the name of a model
* class method, the name of a built-in validator, or a validator class name (or its path alias).
- * - on: optional, specifies the [[scenario|scenarios]] (separated by commas) when the validation
+ * - on: optional, specifies the [[scenario|scenarios]] array when the validation
* rule can be applied. If this option is not set, the rule will apply to all scenarios.
* - additional name-value pairs can be specified to initialize the corresponding validator properties.
* Please refer to individual validator class API for possible properties.
@@ -102,35 +117,39 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* function validatorName($attribute, $params)
* ~~~
*
+ * In the above `$attribute` refers to currently validated attribute name while `$params` contains an array of
+ * validator configuration options such as `max` in case of `string` validator. Currently validate attribute value
+ * can be accessed as `$this->[$attribute]`.
+ *
* Yii also provides a set of [[Validator::builtInValidators|built-in validators]].
* They each has an alias name which can be used when specifying a validation rule.
*
* Below are some examples:
*
* ~~~
- * array(
+ * [
* // built-in "required" validator
- * array('username', 'required'),
- * // built-in "length" validator customized with "min" and "max" properties
- * array('username', 'length', 'min' => 3, 'max' => 12),
+ * [['username', 'password'], 'required'],
+ * // built-in "string" validator customized with "min" and "max" properties
+ * ['username', 'string', 'min' => 3, 'max' => 12],
* // built-in "compare" validator that is used in "register" scenario only
- * array('password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'),
+ * ['password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'],
* // an inline validator defined via the "authenticate()" method in the model class
- * array('password', 'authenticate', 'on' => 'login'),
- * // a validator of class "CaptchaValidator"
- * array('captcha', 'CaptchaValidator'),
- * );
+ * ['password', 'authenticate', 'on' => 'login'],
+ * // a validator of class "DateRangeValidator"
+ * ['dateRange', 'DateRangeValidator'],
+ * ];
* ~~~
*
* Note, in order to inherit rules defined in the parent class, a child class needs to
* merge the parent rules with child rules using functions such as `array_merge()`.
*
* @return array validation rules
- * @see scenarios
+ * @see scenarios()
*/
public function rules()
{
- return array();
+ return [];
}
/**
@@ -139,36 +158,70 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* The returned array should be in the following format:
*
* ~~~
- * array(
- * 'scenario1' => array('attribute11', 'attribute12', ...),
- * 'scenario2' => array('attribute21', 'attribute22', ...),
+ * [
+ * 'scenario1' => ['attribute11', 'attribute12', ...],
+ * 'scenario2' => ['attribute21', 'attribute22', ...],
* ...
- * )
+ * ]
* ~~~
*
- * By default, an active attribute that is considered safe and can be massively assigned.
+ * By default, an active attribute is considered safe and can be massively assigned.
* If an attribute should NOT be massively assigned (thus considered unsafe),
* please prefix the attribute with an exclamation character (e.g. '!rank').
*
- * The default implementation of this method will return a 'default' scenario
- * which corresponds to all attributes listed in the validation rules applicable
- * to the 'default' scenario.
+ * The default implementation of this method will return all scenarios found in the [[rules()]]
+ * declaration. A special scenario named [[DEFAULT_SCENARIO]] will contain all attributes
+ * found in the [[rules()]]. Each scenario will be associated with the attributes that
+ * are being validated by the validation rules that apply to the scenario.
*
* @return array a list of scenarios and the corresponding active attributes.
*/
public function scenarios()
{
- $attributes = array();
- foreach ($this->getActiveValidators() as $validator) {
- if ($validator->isActive('default')) {
- foreach ($validator->attributes as $name) {
- $attributes[$name] = true;
+ $scenarios = [self::DEFAULT_SCENARIO => []];
+ foreach ($this->getValidators() as $validator) {
+ foreach ($validator->on as $scenario) {
+ $scenarios[$scenario] = [];
+ }
+ foreach ($validator->except as $scenario) {
+ $scenarios[$scenario] = [];
+ }
+ }
+ $names = array_keys($scenarios);
+
+ foreach ($this->getValidators() as $validator) {
+ if (empty($validator->on) && empty($validator->except)) {
+ foreach ($names as $name) {
+ foreach ($validator->attributes as $attribute) {
+ $scenarios[$name][$attribute] = true;
+ }
+ }
+ } elseif (empty($validator->on)) {
+ foreach ($names as $name) {
+ if (!in_array($name, $validator->except, true)) {
+ foreach ($validator->attributes as $attribute) {
+ $scenarios[$name][$attribute] = true;
+ }
+ }
+ }
+ } else {
+ foreach ($validator->on as $name) {
+ foreach ($validator->attributes as $attribute) {
+ $scenarios[$name][$attribute] = true;
+ }
}
}
}
- return array(
- 'default' => array_keys($attributes),
- );
+
+ foreach ($scenarios as $scenario => $attributes) {
+ if (empty($attributes) && $scenario !== self::DEFAULT_SCENARIO) {
+ unset($scenarios[$scenario]);
+ } else {
+ $scenarios[$scenario] = array_keys($attributes);
+ }
+ }
+
+ return $scenarios;
}
/**
@@ -186,9 +239,8 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
*/
public function formName()
{
- $class = get_class($this);
- $pos = strrpos($class, '\\');
- return $pos === false ? $class : substr($class, $pos + 1);
+ $reflector = new ReflectionClass($this);
+ return $reflector->getShortName();
}
/**
@@ -199,12 +251,11 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
*/
public function attributes()
{
- $class = new \ReflectionClass($this);
- $names = array();
+ $class = new ReflectionClass($this);
+ $names = [];
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
- $name = $property->getName();
if (!$property->isStatic()) {
- $names[] = $name;
+ $names[] = $property->getName();
}
}
return $names;
@@ -224,11 +275,11 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* merge the parent labels with child labels using functions such as `array_merge()`.
*
* @return array attribute labels (name => label)
- * @see generateAttributeLabel
+ * @see generateAttributeLabel()
*/
public function attributeLabels()
{
- return array();
+ return [];
}
/**
@@ -244,17 +295,24 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* after the actual validation, respectively. If [[beforeValidate()]] returns false,
* the validation will be cancelled and [[afterValidate()]] will not be called.
*
- * Errors found during the validation can be retrieved via [[getErrors()]]
- * and [[getError()]].
+ * Errors found during the validation can be retrieved via [[getErrors()]],
+ * [[getFirstErrors()]] and [[getFirstError()]].
*
* @param array $attributes list of attributes that should be validated.
* If this parameter is empty, it means any attribute listed in the applicable
* validation rules should be validated.
* @param boolean $clearErrors whether to call [[clearErrors()]] before performing validation
* @return boolean whether the validation is successful without any error.
+ * @throws InvalidParamException if the current scenario is unknown.
*/
public function validate($attributes = null, $clearErrors = true)
{
+ $scenarios = $this->scenarios();
+ $scenario = $this->getScenario();
+ if (!isset($scenarios[$scenario])) {
+ throw new InvalidParamException("Unknown scenario: $scenario");
+ }
+
if ($clearErrors) {
$this->clearErrors();
}
@@ -263,7 +321,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
}
if ($this->beforeValidate()) {
foreach ($this->getActiveValidators() as $validator) {
- $validator->validate($this, $attributes);
+ $validator->validateAttributes($this, $attributes);
}
$this->afterValidate();
return !$this->hasErrors();
@@ -311,7 +369,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* $model->validators[] = $newValidator;
* ~~~
*
- * @return ArrayObject all the validators declared in the model.
+ * @return ArrayObject|\yii\validators\Validator[] all the validators declared in the model.
*/
public function getValidators()
{
@@ -329,9 +387,8 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
*/
public function getActiveValidators($attribute = null)
{
- $validators = array();
+ $validators = [];
$scenario = $this->getScenario();
- /** @var $validator Validator */
foreach ($this->getValidators() as $validator) {
if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) {
$validators[] = $validator;
@@ -353,7 +410,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
if ($rule instanceof Validator) {
$validators->append($rule);
} elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
- $validator = Validator::createValidator($rule[1], $this, $rule[0], array_slice($rule, 2));
+ $validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2));
$validators->append($validator);
} else {
throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
@@ -384,6 +441,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* Returns a value indicating whether the attribute is safe for massive assignments.
* @param string $attribute attribute name
* @return boolean whether the attribute is safe for massive assignments
+ * @see safeAttributes()
*/
public function isAttributeSafe($attribute)
{
@@ -391,11 +449,22 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
}
/**
+ * Returns a value indicating whether the attribute is active in the current scenario.
+ * @param string $attribute attribute name
+ * @return boolean whether the attribute is active in the current scenario
+ * @see activeAttributes()
+ */
+ public function isAttributeActive($attribute)
+ {
+ return in_array($attribute, $this->activeAttributes(), true);
+ }
+
+ /**
* Returns the text label for the specified attribute.
* @param string $attribute the attribute name
* @return string the attribute label
- * @see generateAttributeLabel
- * @see attributeLabels
+ * @see generateAttributeLabel()
+ * @see attributeLabels()
*/
public function getAttributeLabel($attribute)
{
@@ -416,42 +485,47 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
/**
* Returns the errors for all attribute or a single attribute.
* @param string $attribute attribute name. Use null to retrieve errors for all attributes.
+ * @property array An array of errors for all attributes. Empty array is returned if no error.
+ * The result is a two-dimensional array. See [[getErrors()]] for detailed description.
* @return array errors for all attributes or the specified attribute. Empty array is returned if no error.
* Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:
*
* ~~~
- * array(
- * 'username' => array(
+ * [
+ * 'username' => [
* 'Username is required.',
* 'Username must contain only word characters.',
- * ),
- * 'email' => array(
+ * ],
+ * 'email' => [
* 'Email address is invalid.',
- * )
- * )
+ * ]
+ * ]
* ~~~
*
- * @see getError
+ * @see getFirstErrors()
+ * @see getFirstError()
*/
public function getErrors($attribute = null)
{
if ($attribute === null) {
- return $this->_errors === null ? array() : $this->_errors;
+ return $this->_errors === null ? [] : $this->_errors;
} else {
- return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : array();
+ return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : [];
}
}
/**
* Returns the first error of every attribute in the model.
* @return array the first errors. An empty array will be returned if there is no error.
+ * @see getErrors()
+ * @see getFirstError()
*/
public function getFirstErrors()
{
if (empty($this->_errors)) {
- return array();
+ return [];
} else {
- $errors = array();
+ $errors = [];
foreach ($this->_errors as $attributeErrors) {
if (isset($attributeErrors[0])) {
$errors[] = $attributeErrors[0];
@@ -465,7 +539,8 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* Returns the first error of the specified attribute.
* @param string $attribute attribute name.
* @return string the error message. Null is returned if no error.
- * @see getErrors
+ * @see getErrors()
+ * @see getFirstErrors()
*/
public function getFirstError($attribute)
{
@@ -477,7 +552,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* @param string $attribute attribute name
* @param string $error new error message
*/
- public function addError($attribute, $error)
+ public function addError($attribute, $error = '')
{
$this->_errors[$attribute][] = $error;
}
@@ -489,7 +564,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
public function clearErrors($attribute = null)
{
if ($attribute === null) {
- $this->_errors = array();
+ $this->_errors = [];
} else {
unset($this->_errors[$attribute]);
}
@@ -516,9 +591,9 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* @param array $except list of attributes whose value should NOT be returned.
* @return array attribute values (name => value).
*/
- public function getAttributes($names = null, $except = array())
+ public function getAttributes($names = null, $except = [])
{
- $values = array();
+ $values = [];
if ($names === null) {
$names = $this->attributes();
}
@@ -564,7 +639,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
public function onUnsafeAttribute($name, $value)
{
if (YII_DEBUG) {
- \Yii::info("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.", __METHOD__);
+ Yii::trace("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.", __METHOD__);
}
}
@@ -574,7 +649,7 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
* Scenario affects how validation is performed and which attributes can
* be massively assigned.
*
- * @return string the scenario that this model is in. Defaults to 'default'.
+ * @return string the scenario that this model is in. Defaults to [[DEFAULT_SCENARIO]].
*/
public function getScenario()
{
@@ -583,8 +658,9 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
/**
* Sets the scenario for the model.
+ * Note that this method does not check if the scenario exists or not.
+ * The method [[validate()]] will perform this check.
* @param string $value the scenario that this model is in.
- * @see getScenario
*/
public function setScenario($value)
{
@@ -600,12 +676,9 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
$scenario = $this->getScenario();
$scenarios = $this->scenarios();
if (!isset($scenarios[$scenario])) {
- return array();
- }
- $attributes = array();
- if (isset($scenarios[$scenario]['attributes']) && is_array($scenarios[$scenario]['attributes'])) {
- $scenarios[$scenario] = $scenarios[$scenario]['attributes'];
+ return [];
}
+ $attributes = [];
foreach ($scenarios[$scenario] as $attribute) {
if ($attribute[0] !== '!') {
$attributes[] = $attribute;
@@ -623,13 +696,9 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
$scenario = $this->getScenario();
$scenarios = $this->scenarios();
if (!isset($scenarios[$scenario])) {
- return array();
- }
- if (isset($scenarios[$scenario]['attributes']) && is_array($scenarios[$scenario]['attributes'])) {
- $attributes = $scenarios[$scenario]['attributes'];
- } else {
- $attributes = $scenarios[$scenario];
+ return [];
}
+ $attributes = $scenarios[$scenario];
foreach ($attributes as $i => $attribute) {
if ($attribute[0] === '!') {
$attributes[$i] = substr($attribute, 1);
@@ -639,13 +708,94 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
}
/**
- * Returns the JSON representation of this object.
+ * Populates the model with the data from end user.
+ * The data to be loaded is `$data[formName]`, where `formName` refers to the value of [[formName()]].
+ * If [[formName()]] is empty, the whole `$data` array will be used to populate the model.
+ * The data being populated is subject to the safety check by [[setAttributes()]].
+ * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array
+ * supplied by end user.
+ * @param string $formName the form name to be used for loading the data into the model.
+ * If not set, [[formName()]] will be used.
+ * @return boolean whether the model is successfully populated with some data.
+ */
+ public function load($data, $formName = null)
+ {
+ $scope = $formName === null ? $this->formName() : $formName;
+ if ($scope == '') {
+ $this->setAttributes($data);
+ return true;
+ } elseif (isset($data[$scope])) {
+ $this->setAttributes($data[$scope]);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Populates a set of models with the data from end user.
+ * This method is mainly used to collect tabular data input.
+ * The data to be loaded for each model is `$data[formName][index]`, where `formName`
+ * refers to the value of [[formName()]], and `index` the index of the model in the `$models` array.
+ * If [[formName()]] is empty, `$data[index]` will be used to populate each model.
+ * The data being populated to each model is subject to the safety check by [[setAttributes()]].
+ * @param array $models the models to be populated. Note that all models should have the same class.
+ * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array
+ * supplied by end user.
+ * @return boolean whether the model is successfully populated with some data.
+ */
+ public static function loadMultiple($models, $data)
+ {
+ /** @var Model $model */
+ $model = reset($models);
+ if ($model === false) {
+ return false;
+ }
+ $success = false;
+ $scope = $model->formName();
+ foreach ($models as $i => $model) {
+ if ($scope == '') {
+ if (isset($data[$i])) {
+ $model->setAttributes($data[$i]);
+ $success = true;
+ }
+ } elseif (isset($data[$scope][$i])) {
+ $model->setAttributes($data[$scope][$i]);
+ $success = true;
+ }
+ }
+ return $success;
+ }
+
+ /**
+ * Validates multiple models.
+ * This method will validate every model. The models being validated may
+ * be of the same or different types.
+ * @param array $models the models to be validated
+ * @param array $attributes list of attributes that should be validated.
+ * If this parameter is empty, it means any attribute listed in the applicable
+ * validation rules should be validated.
+ * @return boolean whether all models are valid. False will be returned if one
+ * or multiple models have validation error.
+ */
+ public static function validateMultiple($models, $attributes = null)
+ {
+ $valid = true;
+ /** @var Model $model */
+ foreach ($models as $model) {
+ $valid = $model->validate($attributes) && $valid;
+ }
+ return $valid;
+ }
+
+ /**
+ * Converts the object into an array.
* The default implementation will return [[attributes]].
- * @return string the JSON representation of this object.
+ * @return array the array representation of the object
*/
- public function toJson()
+ public function toArray()
{
- return Json::encode($this->getAttributes());
+ return $this->getAttributes();
}
/**
diff --git a/framework/yii/base/Module.php b/framework/yii/base/Module.php
index cc7c849..126791b 100644
--- a/framework/yii/base/Module.php
+++ b/framework/yii/base/Module.php
@@ -20,16 +20,16 @@ use Yii;
* [[components|Components]] may be registered with the module so that they are globally
* accessible within the module.
*
- * @property string $uniqueId An ID that uniquely identifies this module among all modules within
- * the current application.
- * @property string $basePath The root directory of the module. Defaults to the directory containing the module class.
- * @property string $controllerPath The directory containing the controller classes. Defaults to "[[basePath]]/controllers".
- * @property string $viewPath The directory containing the view files within this module. Defaults to "[[basePath]]/views".
- * @property string $layoutPath The directory containing the layout view files within this module. Defaults to "[[viewPath]]/layouts".
- * @property array $modules The configuration of the currently installed modules (module ID => configuration).
- * @property array $components The components (indexed by their IDs) registered within this module.
- * @property array $import List of aliases to be imported. This property is write-only.
- * @property array $aliases List of aliases to be defined. This property is write-only.
+ * @property array $aliases List of path aliases to be defined. The array keys are alias names (must start
+ * with '@') and the array values are the corresponding paths or aliases. See [[setAliases()]] for an example.
+ * This property is write-only.
+ * @property string $basePath The root directory of the module.
+ * @property array $components The components (indexed by their IDs).
+ * @property string $controllerPath The directory that contains the controller classes.
+ * @property string $layoutPath The root directory of layout files. Defaults to "[[viewPath]]/layouts".
+ * @property array $modules The modules (indexed by their IDs).
+ * @property string $uniqueId The unique ID of the module. This property is read-only.
+ * @property string $viewPath The root directory of view files. Defaults to "[[basePath]]/view".
*
* @author Qiang Xue
* @since 2.0
@@ -37,22 +37,13 @@ use Yii;
abstract class Module extends Component
{
/**
- * @event ActionEvent an event raised before executing a controller action.
- * You may set [[ActionEvent::isValid]] to be false to cancel the action execution.
- */
- const EVENT_BEFORE_ACTION = 'beforeAction';
- /**
- * @event ActionEvent an event raised after executing a controller action.
- */
- const EVENT_AFTER_ACTION = 'afterAction';
- /**
* @var array custom module parameters (name => value).
*/
- public $params = array();
+ public $params = [];
/**
- * @var array the IDs of the components that should be preloaded when this module is created.
+ * @var array the IDs of the components or modules that should be preloaded right after initialization.
*/
- public $preload = array();
+ public $preload = [];
/**
* @var string an ID that uniquely identifies this module among other modules which have the same [[module|parent]].
*/
@@ -71,28 +62,27 @@ abstract class Module extends Component
* @var array mapping from controller ID to controller configurations.
* Each name-value pair specifies the configuration of a single controller.
* A controller configuration can be either a string or an array.
- * If the former, the string should be the class name or path alias of the controller.
+ * If the former, the string should be the fully qualified class name of the controller.
* If the latter, the array must contain a 'class' element which specifies
- * the controller's class name or path alias, and the rest of the name-value pairs
+ * the controller's fully qualified class name, and the rest of the name-value pairs
* in the array are used to initialize the corresponding controller properties. For example,
*
* ~~~
- * array(
- * 'account' => '@app/controllers/UserController',
- * 'article' => array(
- * 'class' => '@app/controllers/PostController',
+ * [
+ * 'account' => 'app\controllers\UserController',
+ * 'article' => [
+ * 'class' => 'app\controllers\PostController',
* 'pageTitle' => 'something new',
- * ),
- * )
+ * ],
+ * ]
* ~~~
*/
- public $controllerMap = array();
+ public $controllerMap = [];
/**
* @var string the namespace that controller classes are in. If not set,
* it will use the "controllers" sub-namespace under the namespace of this module.
* For example, if the namespace of this module is "foo\bar", then the default
* controller namespace would be "foo\bar\controllers".
- * If the module is an application, it will default to "app\controllers".
*/
public $controllerNamespace;
/**
@@ -122,11 +112,11 @@ abstract class Module extends Component
/**
* @var array child modules of this module
*/
- private $_modules = array();
+ private $_modules = [];
/**
* @var array components registered under this module
*/
- private $_components = array();
+ private $_components = [];
/**
* Constructor.
@@ -134,7 +124,7 @@ abstract class Module extends Component
* @param Module $parent the parent module (if any)
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($id, $parent = null, $config = array())
+ public function __construct($id, $parent = null, $config = [])
{
$this->id = $id;
$this->module = $parent;
@@ -176,22 +166,20 @@ abstract class Module extends Component
/**
* Initializes the module.
* This method is called after the module is created and initialized with property values
- * given in configuration. The default implement will create a path alias using the module [[id]]
- * and then call [[preloadComponents()]] to load components that are declared in [[preload]].
+ * given in configuration. The default implementation will call [[preloadComponents()]] to
+ * load components that are declared in [[preload]].
+ *
+ * If you override this method, please make sure you call the parent implementation.
*/
public function init()
{
- $this->preloadComponents();
if ($this->controllerNamespace === null) {
- if ($this instanceof Application) {
- $this->controllerNamespace = 'app\\controllers';
- } else {
- $class = get_class($this);
- if (($pos = strrpos($class, '\\')) !== false) {
- $this->controllerNamespace = substr($class, 0, $pos) . '\\controllers';
- }
+ $class = get_class($this);
+ if (($pos = strrpos($class, '\\')) !== false) {
+ $this->controllerNamespace = substr($class, 0, $pos) . '\\controllers';
}
}
+ $this->preloadComponents();
}
/**
@@ -201,13 +189,7 @@ abstract class Module extends Component
*/
public function getUniqueId()
{
- if ($this instanceof Application) {
- return '';
- } elseif ($this->module) {
- return $this->module->getUniqueId() . '/' . $this->id;
- } else {
- return $this->id;
- }
+ return $this->module ? ltrim($this->module->getUniqueId() . '/' . $this->id, '/') : $this->id;
}
/**
@@ -236,9 +218,6 @@ abstract class Module extends Component
$p = realpath($path);
if ($p !== false && is_dir($p)) {
$this->_basePath = $p;
- if ($this instanceof Application) {
- Yii::setAlias('@app', $p);
- }
} else {
throw new InvalidParamException("The directory does not exist: $path");
}
@@ -262,7 +241,7 @@ abstract class Module extends Component
* Sets the directory that contains the controller classes.
* @param string $path the directory that contains the controller classes.
* This can be either a directory name or a path alias.
- * @throws Exception if the directory is invalid
+ * @throws InvalidParamException if the directory is invalid
*/
public function setControllerPath($path)
{
@@ -285,7 +264,7 @@ abstract class Module extends Component
/**
* Sets the directory that contains the view files.
* @param string $path the root directory of view files.
- * @throws Exception if the directory is invalid
+ * @throws InvalidParamException if the directory is invalid
*/
public function setViewPath($path)
{
@@ -308,7 +287,7 @@ abstract class Module extends Component
/**
* Sets the directory that contains the layout files.
* @param string $path the root directory of layout files.
- * @throws Exception if the directory is invalid
+ * @throws InvalidParamException if the directory is invalid
*/
public function setLayoutPath($path)
{
@@ -319,15 +298,18 @@ abstract class Module extends Component
* Defines path aliases.
* This method calls [[Yii::setAlias()]] to register the path aliases.
* This method is provided so that you can define path aliases when configuring a module.
+ * @property array list of path aliases to be defined. The array keys are alias names
+ * (must start with '@') and the array values are the corresponding paths or aliases.
+ * See [[setAliases()]] for an example.
* @param array $aliases list of path aliases to be defined. The array keys are alias names
* (must start with '@') and the array values are the corresponding paths or aliases.
* For example,
*
* ~~~
- * array(
+ * [
* '@models' => '@app/models', // an existing alias
* '@backend' => __DIR__ . '/../backend', // a directory
- * )
+ * ]
* ~~~
*/
public function setAliases($aliases)
@@ -338,26 +320,40 @@ abstract class Module extends Component
}
/**
- * Checks whether the named module exists.
- * @param string $id module ID
+ * Checks whether the child module of the specified ID exists.
+ * This method supports checking the existence of both child and grand child modules.
+ * @param string $id module ID. For grand child modules, use ID path relative to this module (e.g. `admin/content`).
* @return boolean whether the named module exists. Both loaded and unloaded modules
* are considered.
*/
public function hasModule($id)
{
- return isset($this->_modules[$id]);
+ if (($pos = strpos($id, '/')) !== false) {
+ // sub-module
+ $module = $this->getModule(substr($id, 0, $pos));
+ return $module === null ? false : $module->hasModule(substr($id, $pos + 1));
+ } else {
+ return isset($this->_modules[$id]);
+ }
}
/**
- * Retrieves the named module.
- * @param string $id module ID (case-sensitive)
+ * Retrieves the child module of the specified ID.
+ * This method supports retrieving both child modules and grand child modules.
+ * @param string $id module ID (case-sensitive). To retrieve grand child modules,
+ * use ID path relative to this module (e.g. `admin/content`).
* @param boolean $load whether to load the module if it is not yet loaded.
- * @return Module|null the module instance, null if the module
- * does not exist.
+ * @return Module|null the module instance, null if the module does not exist.
* @see hasModule()
*/
public function getModule($id, $load = true)
{
+ if (($pos = strpos($id, '/')) !== false) {
+ // sub-module
+ $module = $this->getModule(substr($id, 0, $pos));
+ return $module === null ? null : $module->getModule(substr($id, $pos + 1), $load);
+ }
+
if (isset($this->_modules[$id])) {
if ($this->_modules[$id] instanceof Module) {
return $this->_modules[$id];
@@ -399,7 +395,7 @@ abstract class Module extends Component
public function getModules($loadedOnly = false)
{
if ($loadedOnly) {
- $modules = array();
+ $modules = [];
foreach ($this->_modules as $module) {
if ($module instanceof Module) {
$modules[] = $module;
@@ -424,15 +420,13 @@ abstract class Module extends Component
* The following is an example for registering two sub-modules:
*
* ~~~
- * array(
- * 'comment' => array(
+ * [
+ * 'comment' => [
* 'class' => 'app\modules\comment\CommentModule',
* 'db' => 'db',
- * ),
- * 'booking' => array(
- * 'class' => 'app\modules\booking\BookingModule',
- * ),
- * )
+ * ],
+ * 'booking' => ['class' => 'app\modules\booking\BookingModule'],
+ * ]
* ~~~
*
* @param array $modules modules (id => module configuration or instances)
@@ -468,7 +462,6 @@ abstract class Module extends Component
if ($this->_components[$id] instanceof Object) {
return $this->_components[$id];
} elseif ($load) {
- Yii::trace("Loading component: $id", __METHOD__);
return $this->_components[$id] = Yii::createObject($this->_components[$id]);
}
}
@@ -505,7 +498,7 @@ abstract class Module extends Component
public function getComponents($loadedOnly = false)
{
if ($loadedOnly) {
- $components = array();
+ $components = [];
foreach ($this->_components as $component) {
if ($component instanceof Component) {
$components[] = $component;
@@ -530,16 +523,16 @@ abstract class Module extends Component
* The following is an example for setting two components:
*
* ~~~
- * array(
- * 'db' => array(
+ * [
+ * 'db' => [
* 'class' => 'yii\db\Connection',
* 'dsn' => 'sqlite:path/to/file.db',
- * ),
- * 'cache' => array(
+ * ],
+ * 'cache' => [
* 'class' => 'yii\caching\DbCache',
* 'db' => 'db',
- * ),
- * )
+ * ],
+ * ]
* ~~~
*
* @param array $components components (id => component configuration or instance)
@@ -556,11 +549,18 @@ abstract class Module extends Component
/**
* Loads components that are declared in [[preload]].
+ * @throws InvalidConfigException if a component or module to be preloaded is unknown
*/
public function preloadComponents()
{
foreach ($this->preload as $id) {
- $this->getComponent($id);
+ if ($this->hasComponent($id)) {
+ $this->getComponent($id);
+ } elseif ($this->hasModule($id)) {
+ $this->getModule($id);
+ } else {
+ throw new InvalidConfigException("Unknown component or module: $id");
+ }
}
}
@@ -571,22 +571,23 @@ abstract class Module extends Component
* If the route is empty, the method will use [[defaultRoute]].
* @param string $route the route that specifies the action.
* @param array $params the parameters to be passed to the action
- * @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal.
+ * @return mixed the result of the action.
* @throws InvalidRouteException if the requested route cannot be resolved into an action successfully
*/
- public function runAction($route, $params = array())
+ public function runAction($route, $params = [])
{
- $result = $this->createController($route);
- if (is_array($result)) {
- /** @var $controller Controller */
- list($controller, $actionID) = $result;
+ $parts = $this->createController($route);
+ if (is_array($parts)) {
+ /** @var Controller $controller */
+ list($controller, $actionID) = $parts;
$oldController = Yii::$app->controller;
Yii::$app->controller = $controller;
- $status = $controller->runAction($actionID, $params);
+ $result = $controller->runAction($actionID, $params);
Yii::$app->controller = $oldController;
- return $status;
+ return $result;
} else {
- throw new InvalidRouteException('Unable to resolve the request "' . trim($this->getUniqueId() . '/' . $route, '/') . '".');
+ $id = $this->getUniqueId();
+ throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".');
}
}
@@ -608,10 +609,8 @@ abstract class Module extends Component
if ($route === '') {
$route = $this->defaultRoute;
}
- $route = trim($route, '/');
- if (($pos = strpos($route, '/')) !== false) {
- $id = substr($route, 0, $pos);
- $route = substr($route, $pos + 1);
+ if (strpos($route, '/') !== false) {
+ list ($id, $route) = explode('/', $route, 2);
} else {
$id = $route;
$route = '';
@@ -624,8 +623,8 @@ abstract class Module extends Component
if (isset($this->controllerMap[$id])) {
$controller = Yii::createObject($this->controllerMap[$id], $id, $this);
- } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) {
- $className = str_replace(' ', '', ucwords(implode(' ', explode('-', $id)))) . 'Controller';
+ } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id) {
+ $className = str_replace(' ', '', ucwords(str_replace('-', ' ', $id))) . 'Controller';
$classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php';
if (!is_file($classFile)) {
return false;
@@ -639,29 +638,29 @@ abstract class Module extends Component
}
}
- return isset($controller) ? array($controller, $route) : false;
+ return isset($controller) ? [$controller, $route] : false;
}
/**
- * This method is invoked right before an action is to be executed (after all possible filters.)
+ * This method is invoked right before an action of this module is to be executed (after all possible filters.)
* You may override this method to do last-minute preparation for the action.
+ * Make sure you call the parent implementation so that the relevant event is triggered.
* @param Action $action the action to be executed.
* @return boolean whether the action should continue to be executed.
*/
public function beforeAction($action)
{
- $event = new ActionEvent($action);
- $this->trigger(self::EVENT_BEFORE_ACTION, $event);
- return $event->isValid;
+ return true;
}
/**
- * This method is invoked right after an action is executed.
+ * This method is invoked right after an action of this module has been executed.
* You may override this method to do some postprocessing for the action.
+ * Make sure you call the parent implementation so that the relevant event is triggered.
* @param Action $action the action just executed.
+ * @param mixed $result the action return result.
*/
- public function afterAction($action)
+ public function afterAction($action, &$result)
{
- $this->trigger(self::EVENT_AFTER_ACTION, new ActionEvent($action));
}
}
diff --git a/framework/yii/base/Object.php b/framework/yii/base/Object.php
index 79c1f7b..06fca50 100644
--- a/framework/yii/base/Object.php
+++ b/framework/yii/base/Object.php
@@ -8,14 +8,16 @@
namespace yii\base;
use Yii;
-use yii\helpers\Json;
/**
+ * Object is the base class that implements the *property* feature.
+ *
* @include @yii/base/Object.md
+ *
* @author Qiang Xue
* @since 2.0
*/
-class Object implements Jsonable
+class Object implements Arrayable
{
/**
* @return string the fully qualified name of this class.
@@ -39,7 +41,7 @@ class Object implements Jsonable
*
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($config = array())
+ public function __construct($config = [])
{
if (!empty($config)) {
Yii::configure($this, $config);
@@ -62,16 +64,18 @@ class Object implements Jsonable
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when executing `$value = $object->property;`.
* @param string $name the property name
- * @return mixed the property value, event handlers attached to the event,
- * the named behavior, or the value of a behavior's property
+ * @return mixed the property value
* @throws UnknownPropertyException if the property is not defined
- * @see __set
+ * @throws InvalidCallException if the property is write-only
+ * @see __set()
*/
public function __get($name)
{
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
return $this->$getter();
+ } elseif (method_exists($this, 'set' . $name)) {
+ throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name);
} else {
throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
}
@@ -85,8 +89,8 @@ class Object implements Jsonable
* @param string $name the property name or the event name
* @param mixed $value the property value
* @throws UnknownPropertyException if the property is not defined
- * @throws InvalidCallException if the property is read-only.
- * @see __get
+ * @throws InvalidCallException if the property is read-only
+ * @see __get()
*/
public function __set($name, $value)
{
@@ -143,8 +147,6 @@ class Object implements Jsonable
/**
* Calls the named method which is not a class method.
- * If the name refers to a component property whose value is
- * an anonymous function, the method will execute the function.
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when an unknown method is being invoked.
@@ -155,13 +157,6 @@ class Object implements Jsonable
*/
public function __call($name, $params)
{
- $getter = 'get' . $name;
- if (method_exists($this, $getter)) {
- $func = $this->$getter();
- if ($func instanceof \Closure) {
- return call_user_func_array($func, $params);
- }
- }
throw new UnknownMethodException('Unknown method: ' . get_class($this) . "::$name()");
}
@@ -171,17 +166,17 @@ class Object implements Jsonable
*
* - the class has a getter or setter method associated with the specified name
* (in this case, property name is case-insensitive);
- * - the class has a member variable with the specified name (when `$checkVar` is true);
+ * - the class has a member variable with the specified name (when `$checkVars` is true);
*
* @param string $name the property name
- * @param boolean $checkVar whether to treat member variables as properties
+ * @param boolean $checkVars whether to treat member variables as properties
* @return boolean whether the property is defined
- * @see canGetProperty
- * @see canSetProperty
+ * @see canGetProperty()
+ * @see canSetProperty()
*/
- public function hasProperty($name, $checkVar = true)
+ public function hasProperty($name, $checkVars = true)
{
- return $this->canGetProperty($name, $checkVar) || $this->canSetProperty($name, false);
+ return $this->canGetProperty($name, $checkVars) || $this->canSetProperty($name, false);
}
/**
@@ -190,16 +185,16 @@ class Object implements Jsonable
*
* - the class has a getter method associated with the specified name
* (in this case, property name is case-insensitive);
- * - the class has a member variable with the specified name (when `$checkVar` is true);
+ * - the class has a member variable with the specified name (when `$checkVars` is true);
*
* @param string $name the property name
- * @param boolean $checkVar whether to treat member variables as properties
+ * @param boolean $checkVars whether to treat member variables as properties
* @return boolean whether the property can be read
- * @see canSetProperty
+ * @see canSetProperty()
*/
- public function canGetProperty($name, $checkVar = true)
+ public function canGetProperty($name, $checkVars = true)
{
- return method_exists($this, 'get' . $name) || $checkVar && property_exists($this, $name);
+ return method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name);
}
/**
@@ -208,25 +203,38 @@ class Object implements Jsonable
*
* - the class has a setter method associated with the specified name
* (in this case, property name is case-insensitive);
- * - the class has a member variable with the specified name (when `$checkVar` is true);
+ * - the class has a member variable with the specified name (when `$checkVars` is true);
*
* @param string $name the property name
- * @param boolean $checkVar whether to treat member variables as properties
+ * @param boolean $checkVars whether to treat member variables as properties
* @return boolean whether the property can be written
- * @see canGetProperty
+ * @see canGetProperty()
+ */
+ public function canSetProperty($name, $checkVars = true)
+ {
+ return method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name);
+ }
+
+ /**
+ * Returns a value indicating whether a method is defined.
+ *
+ * The default implementation is a call to php function `method_exists()`.
+ * You may override this method when you implemented the php magic method `__call()`.
+ * @param string $name the property name
+ * @return boolean whether the property is defined
*/
- public function canSetProperty($name, $checkVar = true)
+ public function hasMethod($name)
{
- return method_exists($this, 'set' . $name) || $checkVar && property_exists($this, $name);
+ return method_exists($this, $name);
}
/**
- * Returns the JSON representation of this object.
- * The default implementation will return all public member variables.
- * @return string the JSON representation of this object.
+ * Converts the object into an array.
+ * The default implementation will return all public property values as an array.
+ * @return array the array representation of the object
*/
- public function toJson()
+ public function toArray()
{
- return Json::encode(Yii::getObjectVars($this));
+ return Yii::getObjectVars($this);
}
}
diff --git a/framework/yii/base/Request.php b/framework/yii/base/Request.php
index 45556ab..cd3ffdc 100644
--- a/framework/yii/base/Request.php
+++ b/framework/yii/base/Request.php
@@ -8,6 +8,11 @@
namespace yii\base;
/**
+ * Request represents a request that is handled by an [[Application]].
+ *
+ * @property boolean $isConsoleRequest The value indicating whether the current request is made via console.
+ * @property string $scriptFile Entry script file path (processed w/ realpath()).
+ *
* @author Qiang Xue
* @since 2.0
*/
diff --git a/framework/yii/base/Response.php b/framework/yii/base/Response.php
index b89b537..1403b69 100644
--- a/framework/yii/base/Response.php
+++ b/framework/yii/base/Response.php
@@ -8,65 +8,23 @@
namespace yii\base;
/**
+ * Response represents the response of an [[Application]] to a [[Request]].
+ *
* @author Qiang Xue
* @since 2.0
*/
class Response extends Component
{
- const EVENT_BEGIN_RESPONSE = 'beginResponse';
- const EVENT_END_RESPONSE = 'endResponse';
-
- /**
- * Starts output buffering
- */
- public function beginOutput()
- {
- ob_start();
- ob_implicit_flush(false);
- }
-
/**
- * Returns contents of the output buffer and discards it
- * @return string output buffer contents
+ * @var integer the exit status. Exit statuses should be in the range 0 to 254.
+ * The status 0 means the program terminates successfully.
*/
- public function endOutput()
- {
- return ob_get_clean();
- }
+ public $exitStatus = 0;
/**
- * Returns contents of the output buffer
- * @return string output buffer contents
+ * Sends the response to client.
*/
- public function getOutput()
- {
- return ob_get_contents();
- }
-
- /**
- * Discards the output buffer
- * @param boolean $all if true recursively discards all output buffers used
- */
- public function cleanOutput($all = true)
- {
- if ($all) {
- for ($level = ob_get_level(); $level > 0; --$level) {
- if (!@ob_end_clean()) {
- ob_clean();
- }
- }
- } else {
- ob_end_clean();
- }
- }
-
- public function begin()
- {
- $this->trigger(self::EVENT_BEGIN_RESPONSE);
- }
-
- public function end()
+ public function send()
{
- $this->trigger(self::EVENT_END_RESPONSE);
}
}
diff --git a/framework/yii/base/Theme.php b/framework/yii/base/Theme.php
index 91c32dc..1d8771f 100644
--- a/framework/yii/base/Theme.php
+++ b/framework/yii/base/Theme.php
@@ -8,40 +8,59 @@
namespace yii\base;
use Yii;
-use yii\base\InvalidConfigException;
use yii\helpers\FileHelper;
/**
* Theme represents an application theme.
*
- * A theme is directory consisting of view and layout files which are meant to replace their
- * non-themed counterparts.
+ * When [[View]] renders a view file, it will check the [[Application::theme|active theme]]
+ * to see if there is a themed version of the view file exists. If so, the themed version will be rendered instead.
*
- * Theme uses [[pathMap]] to achieve the file replacement. A view or layout file will be replaced
- * with its themed version if part of its path matches one of the keys in [[pathMap]].
- * Then the matched part will be replaced with the corresponding array value.
+ * A theme is a directory consisting of view files which are meant to replace their non-themed counterparts.
*
- * For example, if [[pathMap]] is `array('/www/views' => '/www/themes/basic')`,
- * then the themed version for a view file `/www/views/site/index.php` will be
- * `/www/themes/basic/site/index.php`.
+ * Theme uses [[pathMap]] to achieve the view file replacement:
+ *
+ * 1. It first looks for a key in [[pathMap]] that is a substring of the given view file path;
+ * 2. If such a key exists, the corresponding value will be used to replace the corresponding part
+ * in the view file path;
+ * 3. It will then check if the updated view file exists or not. If so, that file will be used
+ * to replace the original view file.
+ * 4. If Step 2 or 3 fails, the original view file will be used.
+ *
+ * For example, if [[pathMap]] is `['/web/views' => '/web/themes/basic']`,
+ * then the themed version for a view file `/web/views/site/index.php` will be
+ * `/web/themes/basic/site/index.php`.
+ *
+ * It is possible to map a single path to multiple paths. For example,
+ *
+ * ~~~
+ * 'pathMap' => [
+ * '/web/views' => [
+ * '/web/themes/christmas',
+ * '/web/themes/basic',
+ * ],
+ * ]
+ * ~~~
+ *
+ * In this case, the themed version could be either `/web/themes/christmas/site/index.php` or
+ * `/web/themes/basic/site/index.php`. The former has precedence over the latter if both files exist.
*
* To use a theme, you should configure the [[View::theme|theme]] property of the "view" application
* component like the following:
*
* ~~~
- * 'view' => array(
- * 'theme' => array(
- * 'basePath' => '@wwwroot/themes/basic',
- * 'baseUrl' => '@www/themes/basic',
- * ),
- * ),
+ * 'view' => [
+ * 'theme' => [
+ * 'basePath' => '@webroot/themes/basic',
+ * 'baseUrl' => '@web/themes/basic',
+ * ],
+ * ],
* ~~~
*
* The above configuration specifies a theme located under the "themes/basic" directory of the Web folder
* that contains the entry script of the application. If your theme is designed to handle modules,
* you may configure the [[pathMap]] property like described above.
*
- *
* @author Qiang Xue
* @since 2.0
*/
@@ -77,16 +96,18 @@ class Theme extends Component
if (empty($this->pathMap)) {
if ($this->basePath !== null) {
$this->basePath = Yii::getAlias($this->basePath);
- $this->pathMap = array(Yii::$app->getBasePath() => $this->basePath);
+ $this->pathMap = [Yii::$app->getBasePath() => [$this->basePath]];
} else {
throw new InvalidConfigException('The "basePath" property must be set.');
}
}
- $paths = array();
- foreach ($this->pathMap as $from => $to) {
+ $paths = [];
+ foreach ($this->pathMap as $from => $tos) {
$from = FileHelper::normalizePath(Yii::getAlias($from));
- $to = FileHelper::normalizePath(Yii::getAlias($to));
- $paths[$from . DIRECTORY_SEPARATOR] = $to . DIRECTORY_SEPARATOR;
+ foreach ((array)$tos as $to) {
+ $to = FileHelper::normalizePath(Yii::getAlias($to));
+ $paths[$from . DIRECTORY_SEPARATOR][] = $to . DIRECTORY_SEPARATOR;
+ }
}
$this->pathMap = $paths;
if ($this->baseUrl === null) {
@@ -105,12 +126,14 @@ class Theme extends Component
public function applyTo($path)
{
$path = FileHelper::normalizePath($path);
- foreach ($this->pathMap as $from => $to) {
+ foreach ($this->pathMap as $from => $tos) {
if (strpos($path, $from) === 0) {
$n = strlen($from);
- $file = $to . substr($path, $n);
- if (is_file($file)) {
- return $file;
+ foreach ($tos as $to) {
+ $file = $to . substr($path, $n);
+ if (is_file($file)) {
+ return $file;
+ }
}
}
}
diff --git a/framework/yii/base/UnknownClassException.php b/framework/yii/base/UnknownClassException.php
index 7b893d4..8ccd09c 100644
--- a/framework/yii/base/UnknownClassException.php
+++ b/framework/yii/base/UnknownClassException.php
@@ -8,7 +8,7 @@
namespace yii\base;
/**
- * UnknownClassException represents an exception caused by accessing an unknown class.
+ * UnknownClassException represents an exception caused by using an unknown class.
*
* @author Qiang Xue
* @since 2.0
diff --git a/framework/yii/base/View.php b/framework/yii/base/View.php
index 219a0fb..47650f0 100644
--- a/framework/yii/base/View.php
+++ b/framework/yii/base/View.php
@@ -8,9 +8,7 @@
namespace yii\base;
use Yii;
-use yii\base\Application;
use yii\helpers\FileHelper;
-use yii\helpers\Html;
use yii\widgets\Block;
use yii\widgets\ContentDecorator;
use yii\widgets\FragmentCache;
@@ -26,11 +24,11 @@ use yii\widgets\FragmentCache;
class View extends Component
{
/**
- * @event ViewEvent an event that is triggered by [[beginPage()]].
+ * @event Event an event that is triggered by [[beginPage()]].
*/
const EVENT_BEGIN_PAGE = 'beginPage';
/**
- * @event ViewEvent an event that is triggered by [[endPage()]].
+ * @event Event an event that is triggered by [[endPage()]].
*/
const EVENT_END_PAGE = 'endPage';
/**
@@ -43,62 +41,23 @@ class View extends Component
const EVENT_AFTER_RENDER = 'afterRender';
/**
- * The location of registered JavaScript code block or files.
- * This means the location is in the head section.
- */
- const POS_HEAD = 1;
- /**
- * The location of registered JavaScript code block or files.
- * This means the location is at the beginning of the body section.
- */
- const POS_BEGIN = 2;
- /**
- * The location of registered JavaScript code block or files.
- * This means the location is at the end of the body section.
- */
- const POS_END = 3;
- /**
- * The location of registered JavaScript code block.
- * This means the JavaScript code block will be enclosed within `jQuery(document).ready()`.
- */
- const POS_READY = 4;
- /**
- * This is internally used as the placeholder for receiving the content registered for the head section.
- */
- const PL_HEAD = '';
- /**
- * This is internally used as the placeholder for receiving the content registered for the beginning of the body section.
- */
- const PL_BODY_BEGIN = '';
- /**
- * This is internally used as the placeholder for receiving the content registered for the end of the body section.
- */
- const PL_BODY_END = '';
-
-
- /**
- * @var object the context under which the [[renderFile()]] method is being invoked.
- * This can be a controller, a widget, or any other object.
+ * @var ViewContextInterface the context under which the [[renderFile()]] method is being invoked.
*/
public $context;
/**
* @var mixed custom parameters that are shared among view templates.
*/
- public $params;
+ public $params = [];
/**
* @var array a list of available renderers indexed by their corresponding supported file extensions.
* Each renderer may be a view renderer object or the configuration for creating the renderer object.
* For example, the following configuration enables both Smarty and Twig view renderers:
*
* ~~~
- * array(
- * 'tpl' => array(
- * 'class' => 'yii\smarty\ViewRenderer',
- * ),
- * 'twig' => array(
- * 'class' => 'yii\twig\ViewRenderer',
- * ),
- * )
+ * [
+ * 'tpl' => ['class' => 'yii\smarty\ViewRenderer'],
+ * 'twig' => ['class' => 'yii\twig\ViewRenderer'],
+ * ]
* ~~~
*
* If no renderer is available for the given view file, the view file will be treated as a normal PHP
@@ -106,6 +65,10 @@ class View extends Component
*/
public $renderers;
/**
+ * @var string the default view file extension. This will be appended to view file names if they don't have file extensions.
+ */
+ public $defaultExtension = 'php';
+ /**
* @var Theme|array the theme object or the configuration array for creating the theme object.
* If not set, it means theming is not enabled.
*/
@@ -122,53 +85,13 @@ class View extends Component
* is used internally to implement the content caching feature. Do not modify it directly.
* @internal
*/
- public $cacheStack = array();
+ public $cacheStack = [];
/**
* @var array a list of placeholders for embedding dynamic contents. This property
* is used internally to implement the content caching feature. Do not modify it directly.
* @internal
*/
- public $dynamicPlaceholders = array();
- /**
- * @var array list of the registered asset bundles. The keys are the bundle names, and the values
- * are booleans indicating whether the bundles have been registered.
- * @see registerAssetBundle
- */
- public $assetBundles;
- /**
- * @var string the page title
- */
- public $title;
- /**
- * @var array the registered meta tags.
- * @see registerMetaTag
- */
- public $metaTags;
- /**
- * @var array the registered link tags.
- * @see registerLinkTag
- */
- public $linkTags;
- /**
- * @var array the registered CSS code blocks.
- * @see registerCss
- */
- public $css;
- /**
- * @var array the registered CSS files.
- * @see registerCssFile
- */
- public $cssFiles;
- /**
- * @var array the registered JS code blocks
- * @see registerJs
- */
- public $js;
- /**
- * @var array the registered JS files.
- * @see registerJsFile
- */
- public $jsFiles;
+ public $dynamicPlaceholders = [];
/**
@@ -188,29 +111,74 @@ class View extends Component
/**
* Renders a view.
*
- * This method delegates the call to the [[context]] object:
+ * The view to be rendered can be specified in one of the following formats:
*
- * - If [[context]] is a controller, the [[Controller::renderPartial()]] method will be called;
- * - If [[context]] is a widget, the [[Widget::render()]] method will be called;
- * - Otherwise, an InvalidCallException exception will be thrown.
+ * - path alias (e.g. "@app/views/site/index");
+ * - absolute path within application (e.g. "//site/index"): the view name starts with double slashes.
+ * The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.
+ * - absolute path within current module (e.g. "/site/index"): the view name starts with a single slash.
+ * The actual view file will be looked for under the [[Module::viewPath|view path]] of [[module]].
+ * - resolving any other format will be performed via [[ViewContext::findViewFile()]].
*
* @param string $view the view name. Please refer to [[Controller::findViewFile()]]
* and [[Widget::findViewFile()]] on how to specify this parameter.
* @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
+ * @param object $context the context that the view should use for rendering the view. If null,
+ * existing [[context]] will be used.
* @return string the rendering result
- * @throws InvalidCallException if [[context]] is neither a controller nor a widget.
* @throws InvalidParamException if the view cannot be resolved or the view file does not exist.
- * @see renderFile
+ * @see renderFile()
*/
- public function render($view, $params = array())
+ public function render($view, $params = [], $context = null)
{
- if ($this->context instanceof Controller) {
- return $this->context->renderPartial($view, $params);
- } elseif ($this->context instanceof Widget) {
- return $this->context->render($view, $params);
+ $viewFile = $this->findViewFile($view, $context);
+ return $this->renderFile($viewFile, $params, $context);
+ }
+
+ /**
+ * Finds the view file based on the given view name.
+ * @param string $view the view name or the path alias of the view file. Please refer to [[render()]]
+ * on how to specify this parameter.
+ * @param object $context the context that the view should be used to search the view file. If null,
+ * existing [[context]] will be used.
+ * @return string the view file path. Note that the file may not exist.
+ * @throws InvalidCallException if [[context]] is required and invalid.
+ */
+ protected function findViewFile($view, $context = null)
+ {
+ if (strncmp($view, '@', 1) === 0) {
+ // e.g. "@app/views/main"
+ $file = Yii::getAlias($view);
+ } elseif (strncmp($view, '//', 2) === 0) {
+ // e.g. "//layouts/main"
+ $file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
+ } elseif (strncmp($view, '/', 1) === 0) {
+ // e.g. "/site/index"
+ if (Yii::$app->controller !== null) {
+ $file = Yii::$app->controller->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
+ } else {
+ throw new InvalidCallException("Unable to locate view file for view '$view': no active controller.");
+ }
} else {
- throw new InvalidCallException('View::render() is not supported for the current context.');
+ // context required
+ if ($context === null) {
+ $context = $this->context;
+ }
+ if ($context instanceof ViewContextInterface) {
+ $file = $context->findViewFile($view);
+ } else {
+ throw new InvalidCallException("Unable to locate view file for view '$view': no active view context.");
+ }
+ }
+
+ if (pathinfo($file, PATHINFO_EXTENSION) !== '') {
+ return $file;
+ }
+ $path = $file . '.' . $this->defaultExtension;
+ if ($this->defaultExtension !== 'php' && !is_file($path)) {
+ $path = $file . '.php';
}
+ return $path;
}
/**
@@ -232,7 +200,7 @@ class View extends Component
* @return string the rendering result
* @throws InvalidParamException if the view file does not exist
*/
- public function renderFile($viewFile, $params = array(), $context = null)
+ public function renderFile($viewFile, $params = [], $context = null)
{
$viewFile = Yii::getAlias($viewFile);
if ($this->theme !== null) {
@@ -251,9 +219,10 @@ class View extends Component
$output = '';
if ($this->beforeRender($viewFile)) {
+ Yii::trace("Rendering view file: $viewFile", __METHOD__);
$ext = pathinfo($viewFile, PATHINFO_EXTENSION);
if (isset($this->renderers[$ext])) {
- if (is_array($this->renderers[$ext])) {
+ if (is_array($this->renderers[$ext]) || is_string($this->renderers[$ext])) {
$this->renderers[$ext] = Yii::createObject($this->renderers[$ext]);
}
/** @var ViewRenderer $renderer */
@@ -315,7 +284,7 @@ class View extends Component
* @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.
* @return string the rendering result
*/
- public function renderPhpFile($_file_, $_params_ = array())
+ public function renderPhpFile($_file_, $_params_ = [])
{
ob_start();
ob_implicit_flush(false);
@@ -380,11 +349,11 @@ class View extends Component
*/
public function beginBlock($id, $renderInPlace = false)
{
- return Block::begin(array(
+ return Block::begin([
'id' => $id,
'renderInPlace' => $renderInPlace,
'view' => $this,
- ));
+ ]);
}
/**
@@ -398,10 +367,10 @@ class View extends Component
/**
* Begins the rendering of content that is to be decorated by the specified view.
* This method can be used to implement nested layout. For example, a layout can be embedded
- * in another layout file specified as '@app/view/layouts/base.php' like the following:
+ * in another layout file specified as '@app/views/layouts/base.php' like the following:
*
* ~~~
- * beginContent('@app/view/layouts/base.php'); ?>
+ * beginContent('@app/views/layouts/base.php'); ?>
* ...layout content here...
* endContent(); ?>
* ~~~
@@ -412,13 +381,13 @@ class View extends Component
* @return ContentDecorator the ContentDecorator widget instance
* @see ContentDecorator
*/
- public function beginContent($viewFile, $params = array())
+ public function beginContent($viewFile, $params = [])
{
- return ContentDecorator::begin(array(
+ return ContentDecorator::begin([
'viewFile' => $viewFile,
'params' => $params,
'view' => $this,
- ));
+ ]);
}
/**
@@ -448,11 +417,11 @@ class View extends Component
* @return boolean whether you should generate the content for caching.
* False if the cached version is available.
*/
- public function beginCache($id, $properties = array())
+ public function beginCache($id, $properties = [])
{
$properties['id'] = $id;
$properties['view'] = $this;
- /** @var $cache FragmentCache */
+ /** @var FragmentCache $cache */
$cache = FragmentCache::begin($properties);
if ($cache->getCachedContent() !== false) {
$this->endCache();
@@ -470,29 +439,8 @@ class View extends Component
FragmentCache::end();
}
-
- private $_assetManager;
-
/**
- * Registers the asset manager being used by this view object.
- * @return \yii\web\AssetManager the asset manager. Defaults to the "assetManager" application component.
- */
- public function getAssetManager()
- {
- return $this->_assetManager ?: Yii::$app->getAssetManager();
- }
-
- /**
- * Sets the asset manager.
- * @param \yii\web\AssetManager $value the asset manager
- */
- public function setAssetManager($value)
- {
- $this->_assetManager = $value;
- }
-
- /**
- * Marks the beginning of an HTML page.
+ * Marks the beginning of a page.
*/
public function beginPage()
{
@@ -503,251 +451,11 @@ class View extends Component
}
/**
- * Marks the ending of an HTML page.
+ * Marks the ending of a page.
*/
public function endPage()
{
$this->trigger(self::EVENT_END_PAGE);
-
- $content = ob_get_clean();
- echo strtr($content, array(
- self::PL_HEAD => $this->renderHeadHtml(),
- self::PL_BODY_BEGIN => $this->renderBodyBeginHtml(),
- self::PL_BODY_END => $this->renderBodyEndHtml(),
- ));
-
- unset(
- $this->assetBundles,
- $this->metaTags,
- $this->linkTags,
- $this->css,
- $this->cssFiles,
- $this->js,
- $this->jsFiles
- );
- }
-
- /**
- * Marks the beginning of an HTML body section.
- */
- public function beginBody()
- {
- echo self::PL_BODY_BEGIN;
- }
-
- /**
- * Marks the ending of an HTML body section.
- */
- public function endBody()
- {
- echo self::PL_BODY_END;
- }
-
- /**
- * Marks the position of an HTML head section.
- */
- public function head()
- {
- echo self::PL_HEAD;
- }
-
- /**
- * Registers the named asset bundle.
- * All dependent asset bundles will be registered.
- * @param string $name the name of the asset bundle.
- * @throws InvalidConfigException if the asset bundle does not exist or a circular dependency is detected
- */
- public function registerAssetBundle($name)
- {
- if (!isset($this->assetBundles[$name])) {
- $am = $this->getAssetManager();
- $bundle = $am->getBundle($name);
- if ($bundle !== null) {
- $this->assetBundles[$name] = false;
- $bundle->registerAssets($this);
- $this->assetBundles[$name] = true;
- } else {
- throw new InvalidConfigException("Unknown asset bundle: $name");
- }
- } elseif ($this->assetBundles[$name] === false) {
- throw new InvalidConfigException("A circular dependency is detected for bundle '$name'.");
- }
- }
-
- /**
- * Registers a meta tag.
- * @param array $options the HTML attributes for the meta tag.
- * @param string $key the key that identifies the meta tag. If two meta tags are registered
- * with the same key, the latter will overwrite the former. If this is null, the new meta tag
- * will be appended to the existing ones.
- */
- public function registerMetaTag($options, $key = null)
- {
- if ($key === null) {
- $this->metaTags[] = Html::tag('meta', '', $options);
- } else {
- $this->metaTags[$key] = Html::tag('meta', '', $options);
- }
- }
-
- /**
- * Registers a link tag.
- * @param array $options the HTML attributes for the link tag.
- * @param string $key the key that identifies the link tag. If two link tags are registered
- * with the same key, the latter will overwrite the former. If this is null, the new link tag
- * will be appended to the existing ones.
- */
- public function registerLinkTag($options, $key = null)
- {
- if ($key === null) {
- $this->linkTags[] = Html::tag('link', '', $options);
- } else {
- $this->linkTags[$key] = Html::tag('link', '', $options);
- }
- }
-
- /**
- * Registers a CSS code block.
- * @param string $css the CSS code block to be registered
- * @param array $options the HTML attributes for the style tag.
- * @param string $key the key that identifies the CSS code block. If null, it will use
- * $css as the key. If two CSS code blocks are registered with the same key, the latter
- * will overwrite the former.
- */
- public function registerCss($css, $options = array(), $key = null)
- {
- $key = $key ?: md5($css);
- $this->css[$key] = Html::style($css, $options);
- }
-
- /**
- * Registers a CSS file.
- * @param string $url the CSS file to be registered.
- * @param array $options the HTML attributes for the link tag.
- * @param string $key the key that identifies the CSS script file. If null, it will use
- * $url as the key. If two CSS files are registered with the same key, the latter
- * will overwrite the former.
- */
- public function registerCssFile($url, $options = array(), $key = null)
- {
- $key = $key ?: $url;
- $this->cssFiles[$key] = Html::cssFile($url, $options);
- }
-
- /**
- * Registers a JS code block.
- * @param string $js the JS code block to be registered
- * @param integer $position the position at which the JS script tag should be inserted
- * in a page. The possible values are:
- *
- * - [[POS_HEAD]]: in the head section
- * - [[POS_BEGIN]]: at the beginning of the body section
- * - [[POS_END]]: at the end of the body section
- * - [[POS_READY]]: enclosed within jQuery(document).ready(). This is the default value.
- * Note that by using this position, the method will automatically register the jquery js file.
- *
- * @param string $key the key that identifies the JS code block. If null, it will use
- * $js as the key. If two JS code blocks are registered with the same key, the latter
- * will overwrite the former.
- */
- public function registerJs($js, $position = self::POS_READY, $key = null)
- {
- $key = $key ?: md5($js);
- $this->js[$position][$key] = $js;
- if ($position === self::POS_READY) {
- $this->registerAssetBundle('yii/jquery');
- }
- }
-
- /**
- * Registers a JS file.
- * Please note that when this file depends on other JS files to be registered before,
- * for example jQuery, you should use [[registerAssetBundle]] instead.
- * @param string $url the JS file to be registered.
- * @param array $options the HTML attributes for the script tag. A special option
- * named "position" is supported which specifies where the JS script tag should be inserted
- * in a page. The possible values of "position" are:
- *
- * - [[POS_HEAD]]: in the head section
- * - [[POS_BEGIN]]: at the beginning of the body section
- * - [[POS_END]]: at the end of the body section. This is the default value.
- *
- * @param string $key the key that identifies the JS script file. If null, it will use
- * $url as the key. If two JS files are registered with the same key, the latter
- * will overwrite the former.
- */
- public function registerJsFile($url, $options = array(), $key = null)
- {
- $position = isset($options['position']) ? $options['position'] : self::POS_END;
- unset($options['position']);
- $key = $key ?: $url;
- $this->jsFiles[$position][$key] = Html::jsFile($url, $options);
- }
-
- /**
- * Renders the content to be inserted in the head section.
- * The content is rendered using the registered meta tags, link tags, CSS/JS code blocks and files.
- * @return string the rendered content
- */
- protected function renderHeadHtml()
- {
- $lines = array();
- if (!empty($this->metaTags)) {
- $lines[] = implode("\n", $this->metaTags);
- }
- if (!empty($this->linkTags)) {
- $lines[] = implode("\n", $this->linkTags);
- }
- if (!empty($this->cssFiles)) {
- $lines[] = implode("\n", $this->cssFiles);
- }
- if (!empty($this->css)) {
- $lines[] = implode("\n", $this->css);
- }
- if (!empty($this->jsFiles[self::POS_HEAD])) {
- $lines[] = implode("\n", $this->jsFiles[self::POS_HEAD]);
- }
- if (!empty($this->js[self::POS_HEAD])) {
- $lines[] = Html::script(implode("\n", $this->js[self::POS_HEAD]), array('type' => 'text/javascript'));
- }
- return empty($lines) ? '' : implode("\n", $lines) . "\n";
- }
-
- /**
- * Renders the content to be inserted at the beginning of the body section.
- * The content is rendered using the registered JS code blocks and files.
- * @return string the rendered content
- */
- protected function renderBodyBeginHtml()
- {
- $lines = array();
- if (!empty($this->jsFiles[self::POS_BEGIN])) {
- $lines[] = implode("\n", $this->jsFiles[self::POS_BEGIN]);
- }
- if (!empty($this->js[self::POS_BEGIN])) {
- $lines[] = Html::script(implode("\n", $this->js[self::POS_BEGIN]), array('type' => 'text/javascript'));
- }
- return empty($lines) ? '' : implode("\n", $lines) . "\n";
- }
-
- /**
- * Renders the content to be inserted at the end of the body section.
- * The content is rendered using the registered JS code blocks and files.
- * @return string the rendered content
- */
- protected function renderBodyEndHtml()
- {
- $lines = array();
- if (!empty($this->jsFiles[self::POS_END])) {
- $lines[] = implode("\n", $this->jsFiles[self::POS_END]);
- }
- if (!empty($this->js[self::POS_END])) {
- $lines[] = Html::script(implode("\n", $this->js[self::POS_END]), array('type' => 'text/javascript'));
- }
- if (!empty($this->js[self::POS_READY])) {
- $js = "jQuery(document).ready(function(){\n" . implode("\n", $this->js[self::POS_READY]) . "\n});";
- $lines[] = Html::script($js, array('type' => 'text/javascript'));
- }
- return empty($lines) ? '' : implode("\n", $lines) . "\n";
+ ob_end_flush();
}
}
diff --git a/framework/yii/base/ViewContextInterface.php b/framework/yii/base/ViewContextInterface.php
new file mode 100644
index 0000000..94f6751
--- /dev/null
+++ b/framework/yii/base/ViewContextInterface.php
@@ -0,0 +1,26 @@
+
+ * @since 2.0
+ */
+interface ViewContextInterface
+{
+ /**
+ * Finds the view file corresponding to the specified relative view name.
+ * @param string $view a relative view name. The name does NOT start with a slash.
+ * @return string the view file path. Note that the file may not exist.
+ */
+ public function findViewFile($view);
+}
diff --git a/framework/yii/base/ViewEvent.php b/framework/yii/base/ViewEvent.php
index f1ee7b9..d02e180 100644
--- a/framework/yii/base/ViewEvent.php
+++ b/framework/yii/base/ViewEvent.php
@@ -8,6 +8,8 @@
namespace yii\base;
/**
+ * ViewEvent represents events triggered by the [[View]] component.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -36,7 +38,7 @@ class ViewEvent extends Event
* @param string $viewFile the view file path that is being rendered by [[View::renderFile()]].
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($viewFile, $config = array())
+ public function __construct($viewFile, $config = [])
{
$this->viewFile = $viewFile;
parent::__construct($config);
diff --git a/framework/yii/base/Widget.php b/framework/yii/base/Widget.php
index 0c948e9..16c13f7 100644
--- a/framework/yii/base/Widget.php
+++ b/framework/yii/base/Widget.php
@@ -8,26 +8,39 @@
namespace yii\base;
use Yii;
+use ReflectionClass;
/**
* Widget is the base class for widgets.
*
+ * @property string $id ID of the widget.
+ * @property \yii\web\View $view The view object that can be used to render views or view files. Note that the
+ * type of this property differs in getter and setter. See [[getView()]] and [[setView()]] for details.
+ * @property string $viewPath The directory containing the view files for this widget. This property is
+ * read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
-class Widget extends Component
+class Widget extends Component implements ViewContextInterface
{
/**
* @var integer a counter used to generate [[id]] for widgets.
* @internal
*/
- public static $_counter = 0;
+ public static $counter = 0;
+ /**
+ * @var string the prefix to the automatically generated widget IDs.
+ * @see [[getId()]]
+ */
+ public static $autoIdPrefix = 'w';
+
/**
* @var Widget[] the widgets that are currently being rendered (not ended). This property
* is maintained by [[begin()]] and [[end()]] methods.
* @internal
*/
- public static $_stack = array();
+ public static $stack = [];
/**
@@ -35,27 +48,27 @@ class Widget extends Component
* This method creates an instance of the calling class. It will apply the configuration
* to the created instance. A matching [[end()]] call should be called later.
* @param array $config name-value pairs that will be used to initialize the object properties
- * @return Widget the newly created widget instance
+ * @return static the newly created widget instance
*/
- public static function begin($config = array())
+ public static function begin($config = [])
{
$config['class'] = get_called_class();
/** @var Widget $widget */
$widget = Yii::createObject($config);
- self::$_stack[] = $widget;
+ self::$stack[] = $widget;
return $widget;
}
/**
* Ends a widget.
* Note that the rendering result of the widget is directly echoed out.
- * @return Widget the widget instance that is ended.
+ * @return static the widget instance that is ended.
* @throws InvalidCallException if [[begin()]] and [[end()]] calls are not properly nested
*/
public static function end()
{
- if (!empty(self::$_stack)) {
- $widget = array_pop(self::$_stack);
+ if (!empty(self::$stack)) {
+ $widget = array_pop(self::$stack);
if (get_class($widget) === get_called_class()) {
$widget->run();
return $widget;
@@ -73,7 +86,7 @@ class Widget extends Component
* @param array $config name-value pairs that will be used to initialize the object properties
* @return string the rendering result of the widget.
*/
- public static function widget($config = array())
+ public static function widget($config = [])
{
ob_start();
ob_implicit_flush(false);
@@ -94,7 +107,7 @@ class Widget extends Component
public function getId($autoGenerate = true)
{
if ($autoGenerate && $this->_id === null) {
- $this->_id = 'w' . self::$_counter++;
+ $this->_id = self::$autoIdPrefix . self::$counter++;
}
return $this->_id;
}
@@ -115,7 +128,7 @@ class Widget extends Component
* The [[render()]] and [[renderFile()]] methods will use
* this view object to implement the actual view rendering.
* If not set, it will default to the "view" application component.
- * @return View the view object that can be used to render views or view files.
+ * @return \yii\web\View the view object that can be used to render views or view files.
*/
public function getView()
{
@@ -160,10 +173,9 @@ class Widget extends Component
* @return string the rendering result.
* @throws InvalidParamException if the view file does not exist.
*/
- public function render($view, $params = array())
+ public function render($view, $params = [])
{
- $viewFile = $this->findViewFile($view);
- return $this->getView()->renderFile($viewFile, $params, $this);
+ return $this->getView()->render($view, $params, $this);
}
/**
@@ -173,7 +185,7 @@ class Widget extends Component
* @return string the rendering result.
* @throws InvalidParamException if the view file does not exist.
*/
- public function renderFile($file, $params = array())
+ public function renderFile($file, $params = [])
{
return $this->getView()->renderFile($file, $params, $this);
}
@@ -185,32 +197,18 @@ class Widget extends Component
*/
public function getViewPath()
{
- $className = get_class($this);
- $class = new \ReflectionClass($className);
+ $class = new ReflectionClass($this);
return dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views';
}
/**
* Finds the view file based on the given view name.
- * @param string $view the view name or the path alias of the view file. Please refer to [[render()]]
- * on how to specify this parameter.
+ * File will be searched under [[viewPath]] directory.
+ * @param string $view the view name.
* @return string the view file path. Note that the file may not exist.
*/
- protected function findViewFile($view)
+ public function findViewFile($view)
{
- if (strncmp($view, '@', 1) === 0) {
- // e.g. "@app/views/main"
- $file = Yii::getAlias($view);
- } elseif (strncmp($view, '//', 2) === 0) {
- // e.g. "//layouts/main"
- $file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
- } elseif (strncmp($view, '/', 1) === 0 && Yii::$app->controller !== null) {
- // e.g. "/site/index"
- $file = Yii::$app->controller->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
- } else {
- $file = $this->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
- }
-
- return pathinfo($file, PATHINFO_EXTENSION) === '' ? $file . '.php' : $file;
+ return $this->getViewPath() . DIRECTORY_SEPARATOR . $view;
}
}
diff --git a/framework/yii/behaviors/AutoTimestamp.php b/framework/yii/behaviors/AutoTimestamp.php
index 7611712..432527a 100644
--- a/framework/yii/behaviors/AutoTimestamp.php
+++ b/framework/yii/behaviors/AutoTimestamp.php
@@ -8,6 +8,7 @@
namespace yii\behaviors;
use yii\base\Behavior;
+use yii\base\Event;
use yii\db\Expression;
use yii\db\ActiveRecord;
@@ -20,11 +21,9 @@ use yii\db\ActiveRecord;
* ~~~
* public function behaviors()
* {
- * return array(
- * 'timestamp' => array(
- * 'class' => 'yii\behaviors\AutoTimestamp',
- * ),
- * );
+ * return [
+ * 'timestamp' => ['class' => 'yii\behaviors\AutoTimestamp'],
+ * ];
* }
* ~~~
*
@@ -40,15 +39,15 @@ class AutoTimestamp extends Behavior
/**
* @var array list of attributes that are to be automatically filled with timestamps.
* The array keys are the ActiveRecord events upon which the attributes are to be filled with timestamps,
- * and the array values are the corresponding attribute to be updated. You can use a string to represent
+ * and the array values are the corresponding attribute(s) to be updated. You can use a string to represent
* a single attribute, or an array to represent a list of attributes.
* The default setting is to update the `create_time` attribute upon AR insertion,
* and update the `update_time` attribute upon AR updating.
*/
- public $attributes = array(
+ public $attributes = [
ActiveRecord::EVENT_BEFORE_INSERT => 'create_time',
ActiveRecord::EVENT_BEFORE_UPDATE => 'update_time',
- );
+ ];
/**
* @var \Closure|Expression The expression that will be used for generating the timestamp.
* This can be either an anonymous function that returns the timestamp value,
@@ -64,28 +63,25 @@ class AutoTimestamp extends Behavior
*/
public function events()
{
- $events = array();
- $behavior = $this;
- foreach ($this->attributes as $event => $attributes) {
- if (!is_array($attributes)) {
- $attributes = array($attributes);
- }
- $events[$event] = function () use ($behavior, $attributes) {
- $behavior->updateTimestamp($attributes);
- };
+ $events = $this->attributes;
+ foreach ($events as $i => $event) {
+ $events[$i] = 'updateTimestamp';
}
return $events;
}
/**
* Updates the attributes with the current timestamp.
- * @param array $attributes list of attributes to be updated.
+ * @param Event $event
*/
- public function updateTimestamp($attributes)
+ public function updateTimestamp($event)
{
- $timestamp = $this->evaluateTimestamp();
- foreach ($attributes as $attribute) {
- $this->owner->$attribute = $timestamp;
+ $attributes = isset($this->attributes[$event->name]) ? (array)$this->attributes[$event->name] : [];
+ if (!empty($attributes)) {
+ $timestamp = $this->evaluateTimestamp();
+ foreach ($attributes as $attribute) {
+ $this->owner->$attribute = $timestamp;
+ }
}
}
diff --git a/framework/yii/bootstrap/Alert.php b/framework/yii/bootstrap/Alert.php
deleted file mode 100644
index f84a70b..0000000
--- a/framework/yii/bootstrap/Alert.php
+++ /dev/null
@@ -1,153 +0,0 @@
- 'Say hello...',
- * 'closeButton' => array(
- * 'label' => '×',
- * 'tag' => 'a',
- * ),
- * ));
- * ```
- *
- * The following example will show the content enclosed between the [[begin()]]
- * and [[end()]] calls within the alert box:
- *
- * ```php
- * Alert::begin(array(
- * 'closeButton' => array(
- * 'label' => '×',
- * ),
- * ));
- *
- * echo 'Say hello...';
- *
- * Alert::end();
- * ```
- *
- * @see http://twitter.github.io/bootstrap/javascript.html#alerts
- * @author Antonio Ramirez
- * @since 2.0
- */
-class Alert extends Widget
-{
- /**
- * @var string the body content in the alert component. Note that anything between
- * the [[begin()]] and [[end()]] calls of the Alert widget will also be treated
- * as the body content, and will be rendered before this.
- */
- public $body;
- /**
- * @var array the options for rendering the close button tag.
- * The close button is displayed in the header of the modal window. Clicking
- * on the button will hide the modal window. If this is null, no close button will be rendered.
- *
- * The following special options are supported:
- *
- * - tag: string, the tag name of the button. Defaults to 'button'.
- * - label: string, the label of the button. Defaults to '×'.
- *
- * The rest of the options will be rendered as the HTML attributes of the button tag.
- * Please refer to the [Alert plugin help](http://twitter.github.com/bootstrap/javascript.html#alerts)
- * for the supported HTML attributes.
- */
- public $closeButton = array();
-
-
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
-
- $this->initOptions();
-
- echo Html::beginTag('div', $this->options) . "\n";
- echo $this->renderBodyBegin() . "\n";
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo "\n" . $this->renderBodyEnd();
- echo "\n" . Html::endTag('div');
-
- $this->registerPlugin('alert');
- }
-
- /**
- * Renders the close button if any before rendering the content.
- * @return string the rendering result
- */
- protected function renderBodyBegin()
- {
- return $this->renderCloseButton();
- }
-
- /**
- * Renders the alert body (if any).
- * @return string the rendering result
- */
- protected function renderBodyEnd()
- {
- return $this->body . "\n";
- }
-
- /**
- * Renders the close button.
- * @return string the rendering result
- */
- protected function renderCloseButton()
- {
- if ($this->closeButton !== null) {
- $tag = ArrayHelper::remove($this->closeButton, 'tag', 'button');
- $label = ArrayHelper::remove($this->closeButton, 'label', '×');
- if ($tag === 'button' && !isset($this->closeButton['type'])) {
- $this->closeButton['type'] = 'button';
- }
- return Html::tag($tag, $label, $this->closeButton);
- } else {
- return null;
- }
- }
-
- /**
- * Initializes the widget options.
- * This method sets the default values for various options.
- */
- protected function initOptions()
- {
- $this->options = array_merge(array(
- 'class' => 'fade in',
- ), $this->options);
-
- $this->addCssClass($this->options, 'alert');
-
- if ($this->closeButton !== null) {
- $this->closeButton = array_merge(array(
- 'data-dismiss' => 'alert',
- 'aria-hidden' => 'true',
- 'class' => 'close',
- ), $this->closeButton);
- }
- }
-}
diff --git a/framework/yii/bootstrap/Button.php b/framework/yii/bootstrap/Button.php
deleted file mode 100644
index 856c420..0000000
--- a/framework/yii/bootstrap/Button.php
+++ /dev/null
@@ -1,62 +0,0 @@
- 'Action',
- * 'options' => array('class' => 'btn-large'),
- * ));
- * ```
- * @see http://twitter.github.io/bootstrap/javascript.html#buttons
- * @author Antonio Ramirez
- * @since 2.0
- */
-class Button extends Widget
-{
- /**
- * @var string the tag to use to render the button
- */
- public $tagName = 'button';
- /**
- * @var string the button label
- */
- public $label = 'Button';
- /**
- * @var boolean whether the label should be HTML-encoded.
- */
- public $encodeLabel = true;
-
-
- /**
- * Initializes the widget.
- * If you override this method, make sure you call the parent implementation first.
- */
- public function init()
- {
- parent::init();
- $this->clientOptions = false;
- $this->addCssClass($this->options, 'btn');
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::tag($this->tagName, $this->encodeLabel ? Html::encode($this->label) : $this->label, $this->options);
- $this->registerPlugin('button');
- }
-}
diff --git a/framework/yii/bootstrap/ButtonDropdown.php b/framework/yii/bootstrap/ButtonDropdown.php
deleted file mode 100644
index fec042e..0000000
--- a/framework/yii/bootstrap/ButtonDropdown.php
+++ /dev/null
@@ -1,128 +0,0 @@
- 'Action',
- * 'dropdown' => array(
- * 'items' => array(
- * array(
- * 'label' => 'DropdownA',
- * 'url' => '/',
- * ),
- * array(
- * 'label' => 'DropdownB',
- * 'url' => '#',
- * ),
- * ),
- * ),
- * ));
- * ```
- * @see http://twitter.github.io/bootstrap/javascript.html#buttons
- * @see http://twitter.github.io/bootstrap/components.html#buttonDropdowns
- * @author Antonio Ramirez
- * @since 2.0
- */
-class ButtonDropdown extends Widget
-{
- /**
- * @var string the button label
- */
- public $label = 'Button';
- /**
- * @var array the HTML attributes of the button.
- */
- public $buttonOptions = array();
- /**
- * @var array the configuration array for [[Dropdown]].
- */
- public $dropdown = array();
- /**
- * @var boolean whether to display a group of split-styled button group.
- */
- public $split = false;
-
-
- /**
- * Initializes the widget.
- * If you override this method, make sure you call the parent implementation first.
- */
- public function init()
- {
- parent::init();
- $this->addCssClass($this->options, 'btn-group');
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::beginTag('div', $this->options) . "\n";
- echo $this->renderButton() . "\n";
- echo $this->renderDropdown() . "\n";
- echo Html::endTag('div') . "\n";
- $this->registerPlugin('button');
- }
-
- /**
- * Generates the button dropdown.
- * @return string the rendering result.
- */
- protected function renderButton()
- {
- $this->addCssClass($this->buttonOptions, 'btn');
- if ($this->split) {
- $tag = 'button';
- $options = $this->buttonOptions;
- $this->buttonOptions['data-toggle'] = 'dropdown';
- $this->addCssClass($this->buttonOptions, 'dropdown-toggle');
- $splitButton = Button::widget(array(
- 'label' => ' ',
- 'encodeLabel' => false,
- 'options' => $this->buttonOptions,
- ));
- } else {
- $tag = 'a';
- $this->label .= ' ';
- $options = $this->buttonOptions;
- if (!isset($options['href'])) {
- $options['href'] = '#';
- }
- $this->addCssClass($options, 'dropdown-toggle');
- $options['data-toggle'] = 'dropdown';
- $splitButton = '';
- }
- return Button::widget(array(
- 'tagName' => $tag,
- 'label' => $this->label,
- 'options' => $options,
- 'encodeLabel' => false,
- )) . "\n" . $splitButton;
- }
-
- /**
- * Generates the dropdown menu.
- * @return string the rendering result.
- */
- protected function renderDropdown()
- {
- $config = $this->dropdown;
- $config['clientOptions'] = false;
- return Dropdown::widget($config);
- }
-}
diff --git a/framework/yii/bootstrap/ButtonGroup.php b/framework/yii/bootstrap/ButtonGroup.php
deleted file mode 100644
index e5bf4e9..0000000
--- a/framework/yii/bootstrap/ButtonGroup.php
+++ /dev/null
@@ -1,98 +0,0 @@
- array(
- * array('label' => 'A'),
- * array('label' => 'B'),
- * )
- * ));
- *
- * // button group with an item as a string
- * echo ButtonGroup::::widget(array(
- * 'items' => array(
- * Button::widget(array('label' => 'A')),
- * array('label' => 'B'),
- * )
- * ));
- * ```
- * @see http://twitter.github.io/bootstrap/javascript.html#buttons
- * @see http://twitter.github.io/bootstrap/components.html#buttonGroups
- * @author Antonio Ramirez
- * @since 2.0
- */
-class ButtonGroup extends Widget
-{
- /**
- * @var array list of buttons. Each array element represents a single button
- * which can be specified as a string or an array of the following structure:
- *
- * - label: string, required, the button label.
- * - options: array, optional, the HTML attributes of the button.
- */
- public $buttons = array();
- /**
- * @var boolean whether to HTML-encode the button labels.
- */
- public $encodeLabels = true;
-
-
- /**
- * Initializes the widget.
- * If you override this method, make sure you call the parent implementation first.
- */
- public function init()
- {
- parent::init();
- $this->clientOptions = false;
- $this->addCssClass($this->options, 'btn-group');
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::tag('div', $this->renderButtons(), $this->options);
- $this->registerPlugin('button');
- }
-
- /**
- * Generates the buttons that compound the group as specified on [[items]].
- * @return string the rendering result.
- */
- protected function renderButtons()
- {
- $buttons = array();
- foreach ($this->buttons as $button) {
- if (is_array($button)) {
- $label = ArrayHelper::getValue($button, 'label');
- $options = ArrayHelper::getValue($button, 'options');
- $buttons[] = Button::widget(array(
- 'label' => $label,
- 'options' => $options,
- 'encodeLabel' => $this->encodeLabels
- ));
- } else {
- $buttons[] = $button;
- }
- }
- return implode("\n", $buttons);
- }
-}
diff --git a/framework/yii/bootstrap/Carousel.php b/framework/yii/bootstrap/Carousel.php
deleted file mode 100644
index f8904fa..0000000
--- a/framework/yii/bootstrap/Carousel.php
+++ /dev/null
@@ -1,172 +0,0 @@
- array(
- * // the item contains only the image
- * ' ',
- * // equivalent to the above
- * array(
- * 'content' => ' ',
- * ),
- * // the item contains both the image and the caption
- * array(
- * 'content' => ' ',
- * 'caption' => 'This is title This is the caption text
',
- * 'options' => array(...),
- * ),
- * )
- * ));
- * ```
- *
- * @see http://twitter.github.io/bootstrap/javascript.html#carousel
- * @author Antonio Ramirez
- * @since 2.0
- */
-class Carousel extends Widget
-{
- /**
- * @var array|boolean the labels for the previous and the next control buttons.
- * If false, it means the previous and the next control buttons should not be displayed.
- */
- public $controls = array('‹', '›');
- /**
- * @var array list of slides in the carousel. Each array element represents a single
- * slide with the following structure:
- *
- * ```php
- * array(
- * // required, slide content (HTML), such as an image tag
- * 'content' => ' ',
- * // optional, the caption (HTML) of the slide
- * 'caption'=> 'This is title This is the caption text
',
- * // optional the HTML attributes of the slide container
- * 'options' => array(),
- * )
- * ```
- */
- public $items = array();
-
-
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
- $this->addCssClass($this->options, 'carousel');
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::beginTag('div', $this->options) . "\n";
- echo $this->renderIndicators() . "\n";
- echo $this->renderItems() . "\n";
- echo $this->renderControls() . "\n";
- echo Html::endTag('div') . "\n";
- $this->registerPlugin('carousel');
- }
-
- /**
- * Renders carousel indicators.
- * @return string the rendering result
- */
- public function renderIndicators()
- {
- $indicators = array();
- for ($i = 0, $count = count($this->items); $i < $count; $i++) {
- $options = array('data-target' => '#' . $this->options['id'], 'data-slide-to' => $i);
- if ($i === 0) {
- $this->addCssClass($options, 'active');
- }
- $indicators[] = Html::tag('li', '', $options);
- }
- return Html::tag('ol', implode("\n", $indicators), array('class' => 'carousel-indicators'));
- }
-
- /**
- * Renders carousel items as specified on [[items]].
- * @return string the rendering result
- */
- public function renderItems()
- {
- $items = array();
- for ($i = 0, $count = count($this->items); $i < $count; $i++) {
- $items[] = $this->renderItem($this->items[$i], $i);
- }
- return Html::tag('div', implode("\n", $items), array('class' => 'carousel-inner'));
- }
-
- /**
- * Renders a single carousel item
- * @param string|array $item a single item from [[items]]
- * @param integer $index the item index as the first item should be set to `active`
- * @return string the rendering result
- * @throws InvalidConfigException if the item is invalid
- */
- public function renderItem($item, $index)
- {
- if (is_string($item)) {
- $content = $item;
- $caption = null;
- $options = array();
- } elseif (isset($item['content'])) {
- $content = $item['content'];
- $caption = ArrayHelper::getValue($item, 'caption');
- if ($caption !== null) {
- $caption = Html::tag('div', $caption, array('class' => 'carousel-caption'));
- }
- $options = ArrayHelper::getValue($item, 'options', array());
- } else {
- throw new InvalidConfigException('The "content" option is required.');
- }
-
- $this->addCssClass($options, 'item');
- if ($index === 0) {
- $this->addCssClass($options, 'active');
- }
-
- return Html::tag('div', $content . "\n" . $caption, $options);
- }
-
- /**
- * Renders previous and next control buttons.
- * @throws InvalidConfigException if [[controls]] is invalid.
- */
- public function renderControls()
- {
- if (isset($this->controls[0], $this->controls[1])) {
- return Html::a($this->controls[0], '#' . $this->options['id'], array(
- 'class' => 'left carousel-control',
- 'data-slide' => 'prev',
- )) . "\n"
- . Html::a($this->controls[1], '#' . $this->options['id'], array(
- 'class' => 'right carousel-control',
- 'data-slide' => 'next',
- ));
- } elseif ($this->controls === false) {
- return '';
- } else {
- throw new InvalidConfigException('The "controls" property must be either false or an array of two elements.');
- }
- }
-}
diff --git a/framework/yii/bootstrap/Collapse.php b/framework/yii/bootstrap/Collapse.php
deleted file mode 100644
index fdcaae1..0000000
--- a/framework/yii/bootstrap/Collapse.php
+++ /dev/null
@@ -1,133 +0,0 @@
- array(
- * // equivalent to the above
- * 'Collapsible Group Item #1' => array(
- * 'content' => 'Anim pariatur cliche...',
- * // open its content by default
- * 'contentOptions' => array('class'=>'in')
- * ),
- * // another group item
- * 'Collapsible Group Item #2' => array(
- * 'content' => 'Anim pariatur cliche...',
- * 'contentOptions' => array(...),
- * 'options' => array(...),
- * ),
- * )
- * ));
- * ```
- *
- * @see http://twitter.github.io/bootstrap/javascript.html#collapse
- * @author Antonio Ramirez
- * @since 2.0
- */
-class Collapse extends Widget
-{
- /**
- * @var array list of groups in the collapse widget. Each array element represents a single
- * group with the following structure:
- *
- * ```php
- * // item key is the actual group header
- * 'Collapsible Group Item #1' => array(
- * // required, the content (HTML) of the group
- * 'content' => 'Anim pariatur cliche...',
- * // optional the HTML attributes of the content group
- * 'contentOptions'=> array(),
- * // optional the HTML attributes of the group
- * 'options'=> array(),
- * )
- * ```
- */
- public $items = array();
-
-
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
- $this->addCssClass($this->options, 'accordion');
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::beginTag('div', $this->options) . "\n";
- echo $this->renderItems() . "\n";
- echo Html::endTag('div') . "\n";
- $this->registerPlugin('collapse');
- }
-
- /**
- * Renders collapsible items as specified on [[items]].
- * @return string the rendering result
- */
- public function renderItems()
- {
- $items = array();
- $index = 0;
- foreach ($this->items as $header => $item) {
- $options = ArrayHelper::getValue($item, 'options', array());
- $this->addCssClass($options, 'accordion-group');
- $items[] = Html::tag('div', $this->renderItem($header, $item, ++$index), $options);
- }
-
- return implode("\n", $items);
- }
-
- /**
- * Renders a single collapsible item group
- * @param string $header a label of the item group [[items]]
- * @param array $item a single item from [[items]]
- * @param integer $index the item index as each item group content must have an id
- * @return string the rendering result
- * @throws InvalidConfigException
- */
- public function renderItem($header, $item, $index)
- {
- if (isset($item['content'])) {
- $id = $this->options['id'] . '-collapse' . $index;
- $options = ArrayHelper::getValue($item, 'contentOptions', array());
- $options['id'] = $id;
- $this->addCssClass($options, 'accordion-body collapse');
-
- $header = Html::a($header, '#' . $id, array(
- 'class' => 'accordion-toggle',
- 'data-toggle' => 'collapse',
- 'data-parent' => '#' . $this->options['id']
- )) . "\n";
-
- $content = Html::tag('div', $item['content'], array('class' => 'accordion-inner')) . "\n";
- } else {
- throw new InvalidConfigException('The "content" option is required.');
- }
- $group = array();
-
- $group[] = Html::tag('div', $header, array('class' => 'accordion-heading'));
- $group[] = Html::tag('div', $content, $options);
-
- return implode("\n", $group);
- }
-}
diff --git a/framework/yii/bootstrap/Dropdown.php b/framework/yii/bootstrap/Dropdown.php
deleted file mode 100644
index 827e6cc..0000000
--- a/framework/yii/bootstrap/Dropdown.php
+++ /dev/null
@@ -1,94 +0,0 @@
-
- * @since 2.0
- */
-class Dropdown extends Widget
-{
- /**
- * @var array list of menu items in the dropdown. Each array element represents a single
- * menu with the following structure:
- * - label: string, required, the label of the item link
- * - url: string, optional, the url of the item link. Defaults to "#".
- * - linkOptions: array, optional, the HTML attributes of the item link.
- * - options: array, optional, the HTML attributes of the item.
- * - items: array, optional, the dropdown items configuration array. if `items` is set, then `url` of the parent
- * item will be ignored and automatically set to "#"
- *
- * @see https://github.com/twitter/bootstrap/issues/5050#issuecomment-11741727
- */
- public $items = array();
- /**
- * @var boolean whether the labels for header items should be HTML-encoded.
- */
- public $encodeLabels = true;
-
-
- /**
- * Initializes the widget.
- * If you override this method, make sure you call the parent implementation first.
- */
- public function init()
- {
- parent::init();
- $this->addCssClass($this->options, 'dropdown-menu');
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo $this->renderItems($this->items);
- $this->registerPlugin('dropdown');
- }
-
- /**
- * Renders menu items.
- * @param array $items the menu items to be rendered
- * @return string the rendering result.
- * @throws InvalidConfigException if the label option is not specified in one of the items.
- */
- protected function renderItems($items)
- {
- $lines = array();
- foreach ($items as $item) {
- if (is_string($item)) {
- $lines[] = $item;
- continue;
- }
- if (!isset($item['label'])) {
- throw new InvalidConfigException("The 'label' option is required.");
- }
- $label = $this->encodeLabels ? Html::encode($item['label']) : $item['label'];
- $options = ArrayHelper::getValue($item, 'options', array());
- $linkOptions = ArrayHelper::getValue($item, 'linkOptions', array());
- $linkOptions['tabindex'] = '-1';
-
- if (isset($item['items'])) {
- $this->addCssClass($options, 'dropdown-submenu');
- $content = Html::a($label, '#', $linkOptions) . $this->renderItems($item['items']);
- } else {
- $content = Html::a($label, ArrayHelper::getValue($item, 'url', '#'), $linkOptions);
- }
- $lines[] = Html::tag('li', $content, $options);
- }
-
- return Html::tag('ul', implode("\n", $lines), $this->options);
- }
-}
diff --git a/framework/yii/bootstrap/Modal.php b/framework/yii/bootstrap/Modal.php
deleted file mode 100644
index 0608fbe..0000000
--- a/framework/yii/bootstrap/Modal.php
+++ /dev/null
@@ -1,223 +0,0 @@
- 'Hello world ',
- * 'toggleButton' => array(
- * 'label' => 'click me',
- * ),
- * ));
- *
- * echo 'Say hello...';
- *
- * Modal::end();
- * ~~~
- *
- * @see http://twitter.github.io/bootstrap/javascript.html#modals
- * @author Antonio Ramirez
- * @author Qiang Xue
- * @since 2.0
- */
-class Modal extends Widget
-{
- /**
- * @var string the header content in the modal window.
- */
- public $header;
- /**
- * @var string the footer content in the modal window.
- */
- public $footer;
- /**
- * @var array the options for rendering the close button tag.
- * The close button is displayed in the header of the modal window. Clicking
- * on the button will hide the modal window. If this is null, no close button will be rendered.
- *
- * The following special options are supported:
- *
- * - tag: string, the tag name of the button. Defaults to 'button'.
- * - label: string, the label of the button. Defaults to '×'.
- *
- * The rest of the options will be rendered as the HTML attributes of the button tag.
- * Please refer to the [Modal plugin help](http://twitter.github.com/bootstrap/javascript.html#modals)
- * for the supported HTML attributes.
- */
- public $closeButton = array();
- /**
- * @var array the options for rendering the toggle button tag.
- * The toggle button is used to toggle the visibility of the modal window.
- * If this property is null, no toggle button will be rendered.
- *
- * The following special options are supported:
- *
- * - tag: string, the tag name of the button. Defaults to 'button'.
- * - label: string, the label of the button. Defaults to 'Show'.
- *
- * The rest of the options will be rendered as the HTML attributes of the button tag.
- * Please refer to the [Modal plugin help](http://twitter.github.com/bootstrap/javascript.html#modals)
- * for the supported HTML attributes.
- */
- public $toggleButton;
-
-
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
-
- $this->initOptions();
-
- echo $this->renderToggleButton() . "\n";
- echo Html::beginTag('div', $this->options) . "\n";
- echo $this->renderHeader() . "\n";
- echo $this->renderBodyBegin() . "\n";
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo "\n" . $this->renderBodyEnd();
- echo "\n" . $this->renderFooter();
- echo "\n" . Html::endTag('div');
-
- $this->registerPlugin('modal');
- }
-
- /**
- * Renders the header HTML markup of the modal
- * @return string the rendering result
- */
- protected function renderHeader()
- {
- $button = $this->renderCloseButton();
- if ($button !== null) {
- $this->header = $button . "\n" . $this->header;
- }
- if ($this->header !== null) {
- return Html::tag('div', "\n" . $this->header . "\n", array('class' => 'modal-header'));
- } else {
- return null;
- }
- }
-
- /**
- * Renders the opening tag of the modal body.
- * @return string the rendering result
- */
- protected function renderBodyBegin()
- {
- return Html::beginTag('div', array('class' => 'modal-body'));
- }
-
- /**
- * Renders the closing tag of the modal body.
- * @return string the rendering result
- */
- protected function renderBodyEnd()
- {
- return Html::endTag('div');
- }
-
- /**
- * Renders the HTML markup for the footer of the modal
- * @return string the rendering result
- */
- protected function renderFooter()
- {
- if ($this->footer !== null) {
- return Html::tag('div', "\n" . $this->footer . "\n", array('class' => 'modal-footer'));
- } else {
- return null;
- }
- }
-
- /**
- * Renders the toggle button.
- * @return string the rendering result
- */
- protected function renderToggleButton()
- {
- if ($this->toggleButton !== null) {
- $tag = ArrayHelper::remove($this->toggleButton, 'tag', 'button');
- $label = ArrayHelper::remove($this->toggleButton, 'label', 'Show');
- if ($tag === 'button' && !isset($this->toggleButton['type'])) {
- $this->toggleButton['type'] = 'button';
- }
- return Html::tag($tag, $label, $this->toggleButton);
- } else {
- return null;
- }
- }
-
- /**
- * Renders the close button.
- * @return string the rendering result
- */
- protected function renderCloseButton()
- {
- if ($this->closeButton !== null) {
- $tag = ArrayHelper::remove($this->closeButton, 'tag', 'button');
- $label = ArrayHelper::remove($this->closeButton, 'label', '×');
- if ($tag === 'button' && !isset($this->closeButton['type'])) {
- $this->closeButton['type'] = 'button';
- }
- return Html::tag($tag, $label, $this->closeButton);
- } else {
- return null;
- }
- }
-
- /**
- * Initializes the widget options.
- * This method sets the default values for various options.
- */
- protected function initOptions()
- {
- $this->options = array_merge(array(
- 'class' => 'modal hide',
- ), $this->options);
- $this->addCssClass($this->options, 'modal');
-
- $this->clientOptions = array_merge(array(
- 'show' => false,
- ), $this->clientOptions);
-
- if ($this->closeButton !== null) {
- $this->closeButton = array_merge(array(
- 'data-dismiss' => 'modal',
- 'aria-hidden' => 'true',
- 'class' => 'close',
- ), $this->closeButton);
- }
-
- if ($this->toggleButton !== null) {
- $this->toggleButton = array_merge(array(
- 'data-toggle' => 'modal',
- ), $this->toggleButton);
- if (!isset($this->toggleButton['data-target']) && !isset($this->toggleButton['href'])) {
- $this->toggleButton['data-target'] = '#' . $this->options['id'];
- }
- }
- }
-}
diff --git a/framework/yii/bootstrap/Nav.php b/framework/yii/bootstrap/Nav.php
deleted file mode 100644
index 8069699..0000000
--- a/framework/yii/bootstrap/Nav.php
+++ /dev/null
@@ -1,146 +0,0 @@
- array(
- * array(
- * 'label' => 'Home',
- * 'url' => '/',
- * 'linkOptions' => array(...),
- * 'active' => true,
- * ),
- * array(
- * 'label' => 'Dropdown',
- * 'items' => array(
- * array(
- * 'label' => 'Level 1 -DropdownA',
- * 'url' => '#',
- * 'items' => array(
- * array(
- * 'label' => 'Level 2 -DropdownA',
- * 'url' => '#',
- * ),
- * ),
- * ),
- * array(
- * 'label' => 'Level 1 -DropdownB',
- * 'url' => '#',
- * ),
- * ),
- * ),
- * ),
- * ));
- * ```
- *
- * @see http://twitter.github.io/bootstrap/components.html#nav
- * @author Antonio Ramirez
- * @since 2.0
- */
-class Nav extends Widget
-{
- /**
- * @var array list of items in the nav widget. Each array element represents a single
- * menu item with the following structure:
- *
- * - label: string, required, the nav item label.
- * - url: optional, the item's URL. Defaults to "#".
- * - linkOptions: array, optional, the HTML attributes of the item's link.
- * - options: array, optional, the HTML attributes of the item container (LI).
- * - active: boolean, optional, whether the item should be on active state or not.
- * - dropdown: array|string, optional, the configuration array for creating a [[Dropdown]] widget,
- * or a string representing the dropdown menu. Note that Bootstrap does not support sub-dropdown menus.
- */
- public $items = array();
- /**
- * @var boolean whether the nav items labels should be HTML-encoded.
- */
- public $encodeLabels = true;
-
-
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
- $this->addCssClass($this->options, 'nav');
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo $this->renderItems();
- $this->getView()->registerAssetBundle('yii/bootstrap');
- }
-
- /**
- * Renders widget items.
- */
- public function renderItems()
- {
- $items = array();
- foreach ($this->items as $item) {
- $items[] = $this->renderItem($item);
- }
-
- return Html::tag('ul', implode("\n", $items), $this->options);
- }
-
- /**
- * Renders a widget's item.
- * @param mixed $item the item to render.
- * @return string the rendering result.
- * @throws InvalidConfigException
- */
- public function renderItem($item)
- {
- if (is_string($item)) {
- return $item;
- }
- if (!isset($item['label'])) {
- throw new InvalidConfigException("The 'label' option is required.");
- }
- $label = $this->encodeLabels ? Html::encode($item['label']) : $item['label'];
- $options = ArrayHelper::getValue($item, 'options', array());
- $items = ArrayHelper::getValue($item, 'items');
- $url = Html::url(ArrayHelper::getValue($item, 'url', '#'));
- $linkOptions = ArrayHelper::getValue($item, 'linkOptions', array());
-
- if (ArrayHelper::getValue($item, 'active')) {
- $this->addCssClass($options, 'active');
- }
-
- if ($items !== null) {
- $linkOptions['data-toggle'] = 'dropdown';
- $this->addCssClass($options, 'dropdown');
- $this->addCssClass($urlOptions, 'dropdown-toggle');
- $label .= ' ' . Html::tag('b', '', array('class' => 'caret'));
- if (is_array($items)) {
- $items = Dropdown::widget(array(
- 'items' => $items,
- 'clientOptions' => false,
- ));
- }
- }
-
- return Html::tag('li', Html::a($label, $url, $linkOptions) . $items, $options);
- }
-}
diff --git a/framework/yii/bootstrap/NavBar.php b/framework/yii/bootstrap/NavBar.php
deleted file mode 100644
index 17a938c..0000000
--- a/framework/yii/bootstrap/NavBar.php
+++ /dev/null
@@ -1,188 +0,0 @@
- 'NavBar Test',
- * 'items' => array(
- * // a Nav widget
- * array(
- * // defaults to Nav anyway.
- * 'class' => 'yii\bootstrap\Nav',
- * // widget configuration
- * 'options' => array(
- * 'items' => array(
- * array(
- * 'label' => 'Home',
- * 'url' => '/',
- * 'options' => array('class' => 'active'),
- * ),
- * array(
- * 'label' => 'Dropdown',
- * 'content' => new Dropdown(array(
- * 'items' => array(
- * array(
- * 'label' => 'DropdownA',
- * 'url' => '#',
- * ),
- * array(
- * 'label' => 'DropdownB',
- * 'url' => '#'
- * ),
- * )
- * ),
- * ),
- * )
- * ),
- * ),
- * // you can also use strings
- * '',
- * ),
- * ));
- * ```
- *
- * @see http://twitter.github.io/bootstrap/components.html#navbar
- * @author Antonio Ramirez
- * @since 2.0
- */
-class NavBar extends Widget
-{
- /**
- * @var string the text of the brand.
- * @see http://twitter.github.io/bootstrap/components.html#navbar
- */
- public $brandLabel;
- /**
- * @param array|string $url the URL for the brand's hyperlink tag. This parameter will be processed by [[Html::url()]]
- * and will be used for the "href" attribute of the brand link. Defaults to site root.
- */
- public $brandUrl = '/';
- /**
- * @var array the HTML attributes of the brand link.
- */
- public $brandOptions = array();
- /**
- * @var array list of menu items in the navbar widget. Each array element represents a single
- * menu item with the following structure:
- *
- * ```php
- * array(
- * // optional, the menu item class type of the widget to render. Defaults to "Nav" widget.
- * 'class' => 'Menu item class type',
- * // required, the configuration options of the widget.
- * 'options'=> array(...),
- * ),
- * // optionally, you can pass a string
- * '',
- * ```
- *
- * Optionally, you can also use a plain string instead of an array element.
- */
- public $items = array();
-
-
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
- $this->clientOptions = false;
- $this->addCssClass($this->options, 'navbar');
- $this->addCssClass($this->brandOptions, 'brand');
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::beginTag('div', $this->options);
- echo $this->renderItems();
- echo Html::endTag('div');
- $this->getView()->registerAssetBundle(self::$responsive ? 'yii/bootstrap/responsive' : 'yii/bootstrap');
- }
-
- /**
- * Renders the items.
- * @return string the rendering items.
- */
- protected function renderItems()
- {
- $items = array();
- foreach ($this->items as $item) {
- $items[] = $this->renderItem($item);
- }
- $contents = implode("\n", $items);
- $brand = Html::a($this->brandLabel, $this->brandUrl, $this->brandOptions);
-
- if (self::$responsive) {
- $this->getView()->registerAssetBundle('yii/bootstrap/collapse');
- $contents = Html::tag('div',
- $this->renderToggleButton() .
- $brand . "\n" .
- Html::tag('div', $contents, array('class' => 'nav-collapse collapse navbar-collapse')),
- array('class' => 'container'));
- } else {
- $contents = $brand . "\n" . $contents;
- }
-
- return Html::tag('div', $contents, array('class' => 'navbar-inner'));
- }
-
- /**
- * Renders a item. The item can be a string, a custom class or a Nav widget (defaults if no class specified.
- * @param mixed $item the item to render. If array, it is assumed the configuration of a widget being `class`
- * required and if not specified, then defaults to `yii\bootstrap\Nav`.
- * @return string the rendering result.
- * @throws InvalidConfigException
- */
- protected function renderItem($item)
- {
- if (is_string($item)) {
- return $item;
- }
- $config = ArrayHelper::getValue($item, 'options', array());
- $config['clientOptions'] = false;
-
- $class = ArrayHelper::getValue($item, 'class', 'yii\bootstrap\Nav');
-
- return $class::widget($config);
- }
-
- /**
- * Renders collapsible toggle button.
- * @return string the rendering toggle button.
- */
- protected function renderToggleButton()
- {
- $items = array();
- for ($i = 0; $i < 3; $i++) {
- $items[] = Html::tag('span', '', array('class' => 'icon-bar'));
- }
- return Html::a(implode("\n", $items), null, array(
- 'class' => 'btn btn-navbar',
- 'data-toggle' => 'collapse',
- 'data-target' => 'div.navbar-collapse',
- ));
- }
-}
diff --git a/framework/yii/bootstrap/Progress.php b/framework/yii/bootstrap/Progress.php
deleted file mode 100644
index 7c0473e..0000000
--- a/framework/yii/bootstrap/Progress.php
+++ /dev/null
@@ -1,146 +0,0 @@
- 60,
- * 'label' => 'test',
- * ));
- *
- * // styled
- * echo Progress::widget(array(
- * 'percent' => 65,
- * 'barOptions' => array('class' => 'bar-danger')
- * ));
- *
- * // striped
- * echo Progress::widget(array(
- * 'percent' => 70,
- * 'barOptions' => array('class' => 'bar-warning'),
- * 'options' => array('class' => 'progress-striped')
- * ));
- *
- * // striped animated
- * echo Progress::widget(array(
- * 'percent' => 70,
- * 'barOptions' => array('class' => 'bar-success'),
- * 'options' => array('class' => 'active progress-striped')
- * ));
- *
- * // stacked bars
- * echo Progress::widget(array(
- * 'bars' => array(
- * array('percent' => 30, 'options' => array('class' => 'bar-danger')),
- * array('percent' => 30, 'label'=>'test', 'options' => array('class' => 'bar-success')),
- * array('percent' => 35, 'options' => array('class' => 'bar-warning'))
- * )
- * ));
- * ```
- * @see http://twitter.github.io/bootstrap/components.html#progress
- * @author Antonio Ramirez
- * @since 2.0
- */
-class Progress extends Widget
-{
- /**
- * @var string the button label
- */
- public $label;
- /**
- * @var integer the amount of progress as a percentage.
- */
- public $percent = 0;
- /**
- * @var array the HTML attributes of the
- */
- public $barOptions = array();
- /**
- * @var array a set of bars that are stacked together to form a single progress bar.
- * Each bar is an array of the following structure:
- *
- * ```php
- * array(
- * // required, the amount of progress as a percentage.
- * 'percent' => 30,
- * // optional, the label to be displayed on the bar
- * 'label' => '30%',
- * // optional, array, additional HTML attributes for the bar tag
- * 'options' => array(),
- * )
- */
- public $bars;
-
-
- /**
- * Initializes the widget.
- * If you override this method, make sure you call the parent implementation first.
- */
- public function init()
- {
- parent::init();
- $this->addCssClass($this->options, 'progress');
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::beginTag('div', $this->options) . "\n";
- echo $this->renderProgress() . "\n";
- echo Html::endTag('div') . "\n";
- $this->getView()->registerAssetBundle(static::$responsive ? 'yii/bootstrap/responsive' : 'yii/bootstrap');
- }
-
- /**
- * Renders the progress.
- * @return string the rendering result.
- * @throws InvalidConfigException if the "percent" option is not set in a stacked progress bar.
- */
- protected function renderProgress()
- {
- if (empty($this->bars)) {
- return $this->renderBar($this->percent, $this->label, $this->barOptions);
- }
- $bars = array();
- foreach ($this->bars as $bar) {
- $label = ArrayHelper::getValue($bar, 'label', '');
- if (!isset($bar['percent'])) {
- throw new InvalidConfigException("The 'percent' option is required.");
- }
- $options = ArrayHelper::getValue($bar, 'options', array());
- $bars[] = $this->renderBar($bar['percent'], $label, $options);
- }
- return implode("\n", $bars);
- }
-
- /**
- * Generates a bar
- * @param int $percent the percentage of the bar
- * @param string $label, optional, the label to display at the bar
- * @param array $options the HTML attributes of the bar
- * @return string the rendering result.
- */
- protected function renderBar($percent, $label = '', $options = array())
- {
- $this->addCssClass($options, 'bar');
- $options['style'] = "width:{$percent}%";
- return Html::tag('div', $label, $options);
- }
-}
diff --git a/framework/yii/bootstrap/Tabs.php b/framework/yii/bootstrap/Tabs.php
deleted file mode 100644
index 4a85b9a..0000000
--- a/framework/yii/bootstrap/Tabs.php
+++ /dev/null
@@ -1,195 +0,0 @@
- array(
- * array(
- * 'label' => 'One',
- * 'content' => 'Anim pariatur cliche...',
- * 'active' => true
- * ),
- * array(
- * 'label' => 'Two',
- * 'content' => 'Anim pariatur cliche...',
- * 'headerOptions' => array(...),
- * 'options' => array('id'=>'myveryownID'),
- * ),
- * array(
- * 'label' => 'Dropdown',
- * 'items' => array(
- * array(
- * 'label' => 'DropdownA',
- * 'content' => 'DropdownA, Anim pariatur cliche...',
- * ),
- * array(
- * 'label' => 'DropdownB',
- * 'content' => 'DropdownB, Anim pariatur cliche...',
- * ),
- * ),
- * ),
- * ),
- * ));
- * ```
- *
- * @see http://twitter.github.io/bootstrap/javascript.html#tabs
- * @author Antonio Ramirez
- * @since 2.0
- */
-class Tabs extends Widget
-{
- /**
- * @var array list of tabs in the tabs widget. Each array element represents a single
- * tab with the following structure:
- *
- * - label: string, required, the tab header label.
- * - headerOptions: array, optional, the HTML attributes of the tab header.
- * - content: array, required if `items` is not set. The content (HTML) of the tab pane.
- * - options: array, optional, the HTML attributes of the tab pane container.
- * - active: boolean, optional, whether the item tab header and pane should be visible or not.
- * - items: array, optional, if not set then `content` will be required. The `items` specify a dropdown items
- * configuration array. Each item can hold two extra keys, besides the above ones:
- * * active: boolean, optional, whether the item tab header and pane should be visible or not.
- * * content: string, required if `items` is not set. The content (HTML) of the tab pane.
- * * contentOptions: optional, array, the HTML attributes of the tab content container.
- */
- public $items = array();
- /**
- * @var array list of HTML attributes for the item container tags. This will be overwritten
- * by the "options" set in individual [[items]]. The following special options are recognized:
- *
- * - tag: string, defaults to "div", the tag name of the item container tags.
- */
- public $itemOptions = array();
- /**
- * @var array list of HTML attributes for the header container tags. This will be overwritten
- * by the "headerOptions" set in individual [[items]].
- */
- public $headerOptions = array();
- /**
- * @var boolean whether the labels for header items should be HTML-encoded.
- */
- public $encodeLabels = true;
-
-
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
- $this->addCssClass($this->options, 'nav nav-tabs');
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo $this->renderItems();
- $this->registerPlugin('tab');
- }
-
- /**
- * Renders tab items as specified on [[items]].
- * @return string the rendering result.
- * @throws InvalidConfigException.
- */
- protected function renderItems()
- {
- $headers = array();
- $panes = array();
- foreach ($this->items as $n => $item) {
- if (!isset($item['label'])) {
- throw new InvalidConfigException("The 'label' option is required.");
- }
- $label = $this->encodeLabels ? Html::encode($item['label']) : $item['label'];
- $headerOptions = array_merge($this->headerOptions, ArrayHelper::getValue($item, 'headerOptions', array()));
-
- if (isset($item['items'])) {
- $label .= ' ';
- $this->addCssClass($headerOptions, 'dropdown');
-
- if ($this->renderDropdown($item['items'], $panes)) {
- $this->addCssClass($headerOptions, 'active');
- }
-
- $header = Html::a($label, "#", array('class' => 'dropdown-toggle', 'data-toggle' => 'dropdown')) . "\n"
- . Dropdown::widget(array('items' => $item['items'], 'clientOptions' => false));
- } elseif (isset($item['content'])) {
- $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', array()));
- $options['id'] = ArrayHelper::getValue($options, 'id', $this->options['id'] . '-tab' . $n);
-
- $this->addCssClass($options, 'tab-pane');
- if (ArrayHelper::remove($item, 'active')) {
- $this->addCssClass($options, 'active');
- $this->addCssClass($headerOptions, 'active');
- }
- $header = Html::a($label, '#' . $options['id'], array('data-toggle' => 'tab', 'tabindex' => '-1'));
- $panes[] = Html::tag('div', $item['content'], $options);
- } else {
- throw new InvalidConfigException("Either the 'content' or 'items' option must be set.");
- }
-
- $headers[] = Html::tag('li', $header, $headerOptions);
- }
-
- return Html::tag('ul', implode("\n", $headers), $this->options) . "\n"
- . Html::tag('div', implode("\n", $panes), array('class' => 'tab-content'));
- }
-
- /**
- * Normalizes dropdown item options by removing tab specific keys `content` and `contentOptions`, and also
- * configure `panes` accordingly.
- * @param array $items the dropdown items configuration.
- * @param array $panes the panes reference array.
- * @return boolean whether any of the dropdown items is `active` or not.
- * @throws InvalidConfigException
- */
- protected function renderDropdown(&$items, &$panes)
- {
- $itemActive = false;
-
- foreach ($items as $n => &$item) {
- if (is_string($item)) {
- continue;
- }
- if (!isset($item['content'])) {
- throw new InvalidConfigException("The 'content' option is required.");
- }
-
- $content = ArrayHelper::remove($item, 'content');
- $options = ArrayHelper::remove($item, 'contentOptions', array());
- $this->addCssClass($options, 'tab-pane');
- if (ArrayHelper::remove($item, 'active')) {
- $this->addCssClass($options, 'active');
- $this->addCssClass($item['options'], 'active');
- $itemActive = true;
- }
-
- $options['id'] = ArrayHelper::getValue($options, 'id', $this->options['id'] . '-dd-tab' . $n);
- $item['url'] = '#' . $options['id'];
- $item['linkOptions']['data-toggle'] = 'tab';
-
- $panes[] = Html::tag('div', $content, $options);
-
- unset($item);
- }
- return $itemActive;
- }
-}
diff --git a/framework/yii/bootstrap/TypeAhead.php b/framework/yii/bootstrap/TypeAhead.php
deleted file mode 100644
index 0704404..0000000
--- a/framework/yii/bootstrap/TypeAhead.php
+++ /dev/null
@@ -1,92 +0,0 @@
- $model,
- * 'attribute' => 'country',
- * 'clientOptions' => array(
- * 'source' => array('USA', 'ESP'),
- * ),
- * ));
- * ```
- *
- * The following example will use the name property instead
- *
- * ```php
- * echo TypeAhead::widget(array(
- * 'name' => 'country',
- * 'clientOptions' => array(
- * 'source' => array('USA', 'ESP'),
- * ),
- * ));
- *```
- *
- * @see http://twitter.github.io/bootstrap/javascript.html#typeahead
- * @author Antonio Ramirez
- * @since 2.0
- */
-class TypeAhead extends Widget
-{
- /**
- * @var \yii\base\Model the data model that this widget is associated with
- */
- public $model;
- /**
- * @var string the model attribute that this widget is associated with
- */
- public $attribute;
- /**
- * @var string the input name. This must be set if [[model]] and [[attribute]] are not set.
- */
- public $name;
- /**
- * @var string the input value.
- */
- public $value;
-
-
- /**
- * Renders the widget
- */
- public function run()
- {
- echo $this->renderField();
- $this->registerPlugin('typeahead');
- }
-
- /**
- * Renders the TypeAhead field. If [[model]] has been specified then it will render an active field.
- * If [[model]] is null or not from an [[Model]] instance, then the field will be rendered according to
- * the [[name]] attribute.
- * @return string the rendering result
- * @throws InvalidConfigException when none of the required attributes are set to render the textInput.
- * That is, if [[model]] and [[attribute]] are not set, then [[name]] is required.
- */
- public function renderField()
- {
- if ($this->model instanceof Model && $this->attribute !== null) {
- return Html::activeTextInput($this->model, $this->attribute, $this->options);
- } elseif ($this->name !== null) {
- return Html::textInput($this->name, $this->value, $this->options);
- } else {
- throw new InvalidConfigException("Either 'name' or 'model' and 'attribute' properties must be specified.");
- }
- }
-}
diff --git a/framework/yii/bootstrap/Widget.php b/framework/yii/bootstrap/Widget.php
deleted file mode 100644
index 48b0331..0000000
--- a/framework/yii/bootstrap/Widget.php
+++ /dev/null
@@ -1,101 +0,0 @@
-
- * @author Qiang Xue
- * @since 2.0
- */
-class Widget extends \yii\base\Widget
-{
- /**
- * @var boolean whether to use the responsive version of Bootstrap.
- */
- public static $responsive = true;
- /**
- * @var array the HTML attributes for the widget container tag.
- */
- public $options = array();
- /**
- * @var array the options for the underlying Bootstrap JS plugin.
- * Please refer to the corresponding Bootstrap plugin Web page for possible options.
- * For example, [this page](http://twitter.github.io/bootstrap/javascript.html#modals) shows
- * how to use the "Modal" plugin and the supported options (e.g. "remote").
- */
- public $clientOptions = array();
- /**
- * @var array the event handlers for the underlying Bootstrap JS plugin.
- * Please refer to the corresponding Bootstrap plugin Web page for possible events.
- * For example, [this page](http://twitter.github.io/bootstrap/javascript.html#modals) shows
- * how to use the "Modal" plugin and the supported events (e.g. "shown").
- */
- public $clientEvents = array();
-
-
- /**
- * Initializes the widget.
- * This method will register the bootstrap asset bundle. If you override this method,
- * make sure you call the parent implementation first.
- */
- public function init()
- {
- parent::init();
- if (!isset($this->options['id'])) {
- $this->options['id'] = $this->getId();
- }
- }
-
- /**
- * Registers a specific Bootstrap plugin and the related events
- * @param string $name the name of the Bootstrap plugin
- */
- protected function registerPlugin($name)
- {
- $id = $this->options['id'];
- $view = $this->getView();
- $view->registerAssetBundle(static::$responsive ? 'yii/bootstrap/responsive' : 'yii/bootstrap');
- $view->registerAssetBundle("yii/bootstrap/$name");
-
- if ($this->clientOptions !== false) {
- $options = empty($this->clientOptions) ? '' : Json::encode($this->clientOptions);
- $js = "jQuery('#$id').$name($options);";
- $view->registerJs($js);
- }
-
- if (!empty($this->clientEvents)) {
- $js = array();
- foreach ($this->clientEvents as $event => $handler) {
- $js[] = "jQuery('#$id').on('$event', $handler);";
- }
- $view->registerJs(implode("\n", $js));
- }
- }
-
- /**
- * Adds a CSS class to the specified options.
- * This method will ensure that the CSS class is unique and the "class" option is properly formatted.
- * @param array $options the options to be modified.
- * @param string $class the CSS class to be added
- */
- protected function addCssClass(&$options, $class)
- {
- if (isset($options['class'])) {
- $classes = preg_split('/\s+/', $options['class'] . ' ' . $class, -1, PREG_SPLIT_NO_EMPTY);
- $options['class'] = implode(' ', array_unique($classes));
- } else {
- $options['class'] = $class;
- }
- }
-}
diff --git a/framework/yii/bootstrap/assets.php b/framework/yii/bootstrap/assets.php
deleted file mode 100644
index 95b8d40..0000000
--- a/framework/yii/bootstrap/assets.php
+++ /dev/null
@@ -1,108 +0,0 @@
- array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- YII_DEBUG ? 'css/bootstrap.css' : 'css/bootstrap.min.css',
- ),
- ),
- 'yii/bootstrap/responsive' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- YII_DEBUG ? 'css/bootstrap-responsive.css' : 'css/bootstrap-responsive.min.css',
- ),
- 'depends' => array('yii/bootstrap'),
- ),
- 'yii/bootstrap/affix' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-affix.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
- 'yii/bootstrap/alert' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-alert.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
- 'yii/bootstrap/button' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-button.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
- 'yii/bootstrap/carousel' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-carousel.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
- 'yii/bootstrap/collapse' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-collapse.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
- 'yii/bootstrap/dropdown' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-dropdown.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
- 'yii/bootstrap/modal' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-modal.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
- 'yii/bootstrap/popover' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-popover.js',
- ),
- 'depends' => array('yii/bootstrap/tooltip'),
- ),
- 'yii/bootstrap/scrollspy' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-scrollspy.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
- 'yii/bootstrap/tab' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-tab.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
- 'yii/bootstrap/tooltip' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-tooltip.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
- 'yii/bootstrap/transition' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-transition.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap'),
- ),
- 'yii/bootstrap/typeahead' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'js/bootstrap-typeahead.js',
- ),
- 'depends' => array('yii/jquery', 'yii/bootstrap', 'yii/bootstrap/transition'),
- ),
-);
diff --git a/framework/yii/bootstrap/assets/css/bootstrap-responsive.css b/framework/yii/bootstrap/assets/css/bootstrap-responsive.css
deleted file mode 100644
index 09e88ce..0000000
--- a/framework/yii/bootstrap/assets/css/bootstrap-responsive.css
+++ /dev/null
@@ -1,1109 +0,0 @@
-/*!
- * Bootstrap Responsive v2.3.2
- *
- * Copyright 2012 Twitter, Inc
- * Licensed under the Apache License v2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Designed and built with all the love in the world @twitter by @mdo and @fat.
- */
-
-.clearfix {
- *zoom: 1;
-}
-
-.clearfix:before,
-.clearfix:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.clearfix:after {
- clear: both;
-}
-
-.hide-text {
- font: 0/0 a;
- color: transparent;
- text-shadow: none;
- background-color: transparent;
- border: 0;
-}
-
-.input-block-level {
- display: block;
- width: 100%;
- min-height: 30px;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-
-@-ms-viewport {
- width: device-width;
-}
-
-.hidden {
- display: none;
- visibility: hidden;
-}
-
-.visible-phone {
- display: none !important;
-}
-
-.visible-tablet {
- display: none !important;
-}
-
-.hidden-desktop {
- display: none !important;
-}
-
-.visible-desktop {
- display: inherit !important;
-}
-
-@media (min-width: 768px) and (max-width: 979px) {
- .hidden-desktop {
- display: inherit !important;
- }
- .visible-desktop {
- display: none !important ;
- }
- .visible-tablet {
- display: inherit !important;
- }
- .hidden-tablet {
- display: none !important;
- }
-}
-
-@media (max-width: 767px) {
- .hidden-desktop {
- display: inherit !important;
- }
- .visible-desktop {
- display: none !important;
- }
- .visible-phone {
- display: inherit !important;
- }
- .hidden-phone {
- display: none !important;
- }
-}
-
-.visible-print {
- display: none !important;
-}
-
-@media print {
- .visible-print {
- display: inherit !important;
- }
- .hidden-print {
- display: none !important;
- }
-}
-
-@media (min-width: 1200px) {
- .row {
- margin-left: -30px;
- *zoom: 1;
- }
- .row:before,
- .row:after {
- display: table;
- line-height: 0;
- content: "";
- }
- .row:after {
- clear: both;
- }
- [class*="span"] {
- float: left;
- min-height: 1px;
- margin-left: 30px;
- }
- .container,
- .navbar-static-top .container,
- .navbar-fixed-top .container,
- .navbar-fixed-bottom .container {
- width: 1170px;
- }
- .span12 {
- width: 1170px;
- }
- .span11 {
- width: 1070px;
- }
- .span10 {
- width: 970px;
- }
- .span9 {
- width: 870px;
- }
- .span8 {
- width: 770px;
- }
- .span7 {
- width: 670px;
- }
- .span6 {
- width: 570px;
- }
- .span5 {
- width: 470px;
- }
- .span4 {
- width: 370px;
- }
- .span3 {
- width: 270px;
- }
- .span2 {
- width: 170px;
- }
- .span1 {
- width: 70px;
- }
- .offset12 {
- margin-left: 1230px;
- }
- .offset11 {
- margin-left: 1130px;
- }
- .offset10 {
- margin-left: 1030px;
- }
- .offset9 {
- margin-left: 930px;
- }
- .offset8 {
- margin-left: 830px;
- }
- .offset7 {
- margin-left: 730px;
- }
- .offset6 {
- margin-left: 630px;
- }
- .offset5 {
- margin-left: 530px;
- }
- .offset4 {
- margin-left: 430px;
- }
- .offset3 {
- margin-left: 330px;
- }
- .offset2 {
- margin-left: 230px;
- }
- .offset1 {
- margin-left: 130px;
- }
- .row-fluid {
- width: 100%;
- *zoom: 1;
- }
- .row-fluid:before,
- .row-fluid:after {
- display: table;
- line-height: 0;
- content: "";
- }
- .row-fluid:after {
- clear: both;
- }
- .row-fluid [class*="span"] {
- display: block;
- float: left;
- width: 100%;
- min-height: 30px;
- margin-left: 2.564102564102564%;
- *margin-left: 2.5109110747408616%;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- }
- .row-fluid [class*="span"]:first-child {
- margin-left: 0;
- }
- .row-fluid .controls-row [class*="span"] + [class*="span"] {
- margin-left: 2.564102564102564%;
- }
- .row-fluid .span12 {
- width: 100%;
- *width: 99.94680851063829%;
- }
- .row-fluid .span11 {
- width: 91.45299145299145%;
- *width: 91.39979996362975%;
- }
- .row-fluid .span10 {
- width: 82.90598290598291%;
- *width: 82.8527914166212%;
- }
- .row-fluid .span9 {
- width: 74.35897435897436%;
- *width: 74.30578286961266%;
- }
- .row-fluid .span8 {
- width: 65.81196581196582%;
- *width: 65.75877432260411%;
- }
- .row-fluid .span7 {
- width: 57.26495726495726%;
- *width: 57.21176577559556%;
- }
- .row-fluid .span6 {
- width: 48.717948717948715%;
- *width: 48.664757228587014%;
- }
- .row-fluid .span5 {
- width: 40.17094017094017%;
- *width: 40.11774868157847%;
- }
- .row-fluid .span4 {
- width: 31.623931623931625%;
- *width: 31.570740134569924%;
- }
- .row-fluid .span3 {
- width: 23.076923076923077%;
- *width: 23.023731587561375%;
- }
- .row-fluid .span2 {
- width: 14.52991452991453%;
- *width: 14.476723040552828%;
- }
- .row-fluid .span1 {
- width: 5.982905982905983%;
- *width: 5.929714493544281%;
- }
- .row-fluid .offset12 {
- margin-left: 105.12820512820512%;
- *margin-left: 105.02182214948171%;
- }
- .row-fluid .offset12:first-child {
- margin-left: 102.56410256410257%;
- *margin-left: 102.45771958537915%;
- }
- .row-fluid .offset11 {
- margin-left: 96.58119658119658%;
- *margin-left: 96.47481360247316%;
- }
- .row-fluid .offset11:first-child {
- margin-left: 94.01709401709402%;
- *margin-left: 93.91071103837061%;
- }
- .row-fluid .offset10 {
- margin-left: 88.03418803418803%;
- *margin-left: 87.92780505546462%;
- }
- .row-fluid .offset10:first-child {
- margin-left: 85.47008547008548%;
- *margin-left: 85.36370249136206%;
- }
- .row-fluid .offset9 {
- margin-left: 79.48717948717949%;
- *margin-left: 79.38079650845607%;
- }
- .row-fluid .offset9:first-child {
- margin-left: 76.92307692307693%;
- *margin-left: 76.81669394435352%;
- }
- .row-fluid .offset8 {
- margin-left: 70.94017094017094%;
- *margin-left: 70.83378796144753%;
- }
- .row-fluid .offset8:first-child {
- margin-left: 68.37606837606839%;
- *margin-left: 68.26968539734497%;
- }
- .row-fluid .offset7 {
- margin-left: 62.393162393162385%;
- *margin-left: 62.28677941443899%;
- }
- .row-fluid .offset7:first-child {
- margin-left: 59.82905982905982%;
- *margin-left: 59.72267685033642%;
- }
- .row-fluid .offset6 {
- margin-left: 53.84615384615384%;
- *margin-left: 53.739770867430444%;
- }
- .row-fluid .offset6:first-child {
- margin-left: 51.28205128205128%;
- *margin-left: 51.175668303327875%;
- }
- .row-fluid .offset5 {
- margin-left: 45.299145299145295%;
- *margin-left: 45.1927623204219%;
- }
- .row-fluid .offset5:first-child {
- margin-left: 42.73504273504273%;
- *margin-left: 42.62865975631933%;
- }
- .row-fluid .offset4 {
- margin-left: 36.75213675213675%;
- *margin-left: 36.645753773413354%;
- }
- .row-fluid .offset4:first-child {
- margin-left: 34.18803418803419%;
- *margin-left: 34.081651209310785%;
- }
- .row-fluid .offset3 {
- margin-left: 28.205128205128204%;
- *margin-left: 28.0987452264048%;
- }
- .row-fluid .offset3:first-child {
- margin-left: 25.641025641025642%;
- *margin-left: 25.53464266230224%;
- }
- .row-fluid .offset2 {
- margin-left: 19.65811965811966%;
- *margin-left: 19.551736679396257%;
- }
- .row-fluid .offset2:first-child {
- margin-left: 17.094017094017094%;
- *margin-left: 16.98763411529369%;
- }
- .row-fluid .offset1 {
- margin-left: 11.11111111111111%;
- *margin-left: 11.004728132387708%;
- }
- .row-fluid .offset1:first-child {
- margin-left: 8.547008547008547%;
- *margin-left: 8.440625568285142%;
- }
- input,
- textarea,
- .uneditable-input {
- margin-left: 0;
- }
- .controls-row [class*="span"] + [class*="span"] {
- margin-left: 30px;
- }
- input.span12,
- textarea.span12,
- .uneditable-input.span12 {
- width: 1156px;
- }
- input.span11,
- textarea.span11,
- .uneditable-input.span11 {
- width: 1056px;
- }
- input.span10,
- textarea.span10,
- .uneditable-input.span10 {
- width: 956px;
- }
- input.span9,
- textarea.span9,
- .uneditable-input.span9 {
- width: 856px;
- }
- input.span8,
- textarea.span8,
- .uneditable-input.span8 {
- width: 756px;
- }
- input.span7,
- textarea.span7,
- .uneditable-input.span7 {
- width: 656px;
- }
- input.span6,
- textarea.span6,
- .uneditable-input.span6 {
- width: 556px;
- }
- input.span5,
- textarea.span5,
- .uneditable-input.span5 {
- width: 456px;
- }
- input.span4,
- textarea.span4,
- .uneditable-input.span4 {
- width: 356px;
- }
- input.span3,
- textarea.span3,
- .uneditable-input.span3 {
- width: 256px;
- }
- input.span2,
- textarea.span2,
- .uneditable-input.span2 {
- width: 156px;
- }
- input.span1,
- textarea.span1,
- .uneditable-input.span1 {
- width: 56px;
- }
- .thumbnails {
- margin-left: -30px;
- }
- .thumbnails > li {
- margin-left: 30px;
- }
- .row-fluid .thumbnails {
- margin-left: 0;
- }
-}
-
-@media (min-width: 768px) and (max-width: 979px) {
- .row {
- margin-left: -20px;
- *zoom: 1;
- }
- .row:before,
- .row:after {
- display: table;
- line-height: 0;
- content: "";
- }
- .row:after {
- clear: both;
- }
- [class*="span"] {
- float: left;
- min-height: 1px;
- margin-left: 20px;
- }
- .container,
- .navbar-static-top .container,
- .navbar-fixed-top .container,
- .navbar-fixed-bottom .container {
- width: 724px;
- }
- .span12 {
- width: 724px;
- }
- .span11 {
- width: 662px;
- }
- .span10 {
- width: 600px;
- }
- .span9 {
- width: 538px;
- }
- .span8 {
- width: 476px;
- }
- .span7 {
- width: 414px;
- }
- .span6 {
- width: 352px;
- }
- .span5 {
- width: 290px;
- }
- .span4 {
- width: 228px;
- }
- .span3 {
- width: 166px;
- }
- .span2 {
- width: 104px;
- }
- .span1 {
- width: 42px;
- }
- .offset12 {
- margin-left: 764px;
- }
- .offset11 {
- margin-left: 702px;
- }
- .offset10 {
- margin-left: 640px;
- }
- .offset9 {
- margin-left: 578px;
- }
- .offset8 {
- margin-left: 516px;
- }
- .offset7 {
- margin-left: 454px;
- }
- .offset6 {
- margin-left: 392px;
- }
- .offset5 {
- margin-left: 330px;
- }
- .offset4 {
- margin-left: 268px;
- }
- .offset3 {
- margin-left: 206px;
- }
- .offset2 {
- margin-left: 144px;
- }
- .offset1 {
- margin-left: 82px;
- }
- .row-fluid {
- width: 100%;
- *zoom: 1;
- }
- .row-fluid:before,
- .row-fluid:after {
- display: table;
- line-height: 0;
- content: "";
- }
- .row-fluid:after {
- clear: both;
- }
- .row-fluid [class*="span"] {
- display: block;
- float: left;
- width: 100%;
- min-height: 30px;
- margin-left: 2.7624309392265194%;
- *margin-left: 2.709239449864817%;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- }
- .row-fluid [class*="span"]:first-child {
- margin-left: 0;
- }
- .row-fluid .controls-row [class*="span"] + [class*="span"] {
- margin-left: 2.7624309392265194%;
- }
- .row-fluid .span12 {
- width: 100%;
- *width: 99.94680851063829%;
- }
- .row-fluid .span11 {
- width: 91.43646408839778%;
- *width: 91.38327259903608%;
- }
- .row-fluid .span10 {
- width: 82.87292817679558%;
- *width: 82.81973668743387%;
- }
- .row-fluid .span9 {
- width: 74.30939226519337%;
- *width: 74.25620077583166%;
- }
- .row-fluid .span8 {
- width: 65.74585635359117%;
- *width: 65.69266486422946%;
- }
- .row-fluid .span7 {
- width: 57.18232044198895%;
- *width: 57.12912895262725%;
- }
- .row-fluid .span6 {
- width: 48.61878453038674%;
- *width: 48.56559304102504%;
- }
- .row-fluid .span5 {
- width: 40.05524861878453%;
- *width: 40.00205712942283%;
- }
- .row-fluid .span4 {
- width: 31.491712707182323%;
- *width: 31.43852121782062%;
- }
- .row-fluid .span3 {
- width: 22.92817679558011%;
- *width: 22.87498530621841%;
- }
- .row-fluid .span2 {
- width: 14.3646408839779%;
- *width: 14.311449394616199%;
- }
- .row-fluid .span1 {
- width: 5.801104972375691%;
- *width: 5.747913483013988%;
- }
- .row-fluid .offset12 {
- margin-left: 105.52486187845304%;
- *margin-left: 105.41847889972962%;
- }
- .row-fluid .offset12:first-child {
- margin-left: 102.76243093922652%;
- *margin-left: 102.6560479605031%;
- }
- .row-fluid .offset11 {
- margin-left: 96.96132596685082%;
- *margin-left: 96.8549429881274%;
- }
- .row-fluid .offset11:first-child {
- margin-left: 94.1988950276243%;
- *margin-left: 94.09251204890089%;
- }
- .row-fluid .offset10 {
- margin-left: 88.39779005524862%;
- *margin-left: 88.2914070765252%;
- }
- .row-fluid .offset10:first-child {
- margin-left: 85.6353591160221%;
- *margin-left: 85.52897613729868%;
- }
- .row-fluid .offset9 {
- margin-left: 79.8342541436464%;
- *margin-left: 79.72787116492299%;
- }
- .row-fluid .offset9:first-child {
- margin-left: 77.07182320441989%;
- *margin-left: 76.96544022569647%;
- }
- .row-fluid .offset8 {
- margin-left: 71.2707182320442%;
- *margin-left: 71.16433525332079%;
- }
- .row-fluid .offset8:first-child {
- margin-left: 68.50828729281768%;
- *margin-left: 68.40190431409427%;
- }
- .row-fluid .offset7 {
- margin-left: 62.70718232044199%;
- *margin-left: 62.600799341718584%;
- }
- .row-fluid .offset7:first-child {
- margin-left: 59.94475138121547%;
- *margin-left: 59.838368402492065%;
- }
- .row-fluid .offset6 {
- margin-left: 54.14364640883978%;
- *margin-left: 54.037263430116376%;
- }
- .row-fluid .offset6:first-child {
- margin-left: 51.38121546961326%;
- *margin-left: 51.27483249088986%;
- }
- .row-fluid .offset5 {
- margin-left: 45.58011049723757%;
- *margin-left: 45.47372751851417%;
- }
- .row-fluid .offset5:first-child {
- margin-left: 42.81767955801105%;
- *margin-left: 42.71129657928765%;
- }
- .row-fluid .offset4 {
- margin-left: 37.01657458563536%;
- *margin-left: 36.91019160691196%;
- }
- .row-fluid .offset4:first-child {
- margin-left: 34.25414364640884%;
- *margin-left: 34.14776066768544%;
- }
- .row-fluid .offset3 {
- margin-left: 28.45303867403315%;
- *margin-left: 28.346655695309746%;
- }
- .row-fluid .offset3:first-child {
- margin-left: 25.69060773480663%;
- *margin-left: 25.584224756083227%;
- }
- .row-fluid .offset2 {
- margin-left: 19.88950276243094%;
- *margin-left: 19.783119783707537%;
- }
- .row-fluid .offset2:first-child {
- margin-left: 17.12707182320442%;
- *margin-left: 17.02068884448102%;
- }
- .row-fluid .offset1 {
- margin-left: 11.32596685082873%;
- *margin-left: 11.219583872105325%;
- }
- .row-fluid .offset1:first-child {
- margin-left: 8.56353591160221%;
- *margin-left: 8.457152932878806%;
- }
- input,
- textarea,
- .uneditable-input {
- margin-left: 0;
- }
- .controls-row [class*="span"] + [class*="span"] {
- margin-left: 20px;
- }
- input.span12,
- textarea.span12,
- .uneditable-input.span12 {
- width: 710px;
- }
- input.span11,
- textarea.span11,
- .uneditable-input.span11 {
- width: 648px;
- }
- input.span10,
- textarea.span10,
- .uneditable-input.span10 {
- width: 586px;
- }
- input.span9,
- textarea.span9,
- .uneditable-input.span9 {
- width: 524px;
- }
- input.span8,
- textarea.span8,
- .uneditable-input.span8 {
- width: 462px;
- }
- input.span7,
- textarea.span7,
- .uneditable-input.span7 {
- width: 400px;
- }
- input.span6,
- textarea.span6,
- .uneditable-input.span6 {
- width: 338px;
- }
- input.span5,
- textarea.span5,
- .uneditable-input.span5 {
- width: 276px;
- }
- input.span4,
- textarea.span4,
- .uneditable-input.span4 {
- width: 214px;
- }
- input.span3,
- textarea.span3,
- .uneditable-input.span3 {
- width: 152px;
- }
- input.span2,
- textarea.span2,
- .uneditable-input.span2 {
- width: 90px;
- }
- input.span1,
- textarea.span1,
- .uneditable-input.span1 {
- width: 28px;
- }
-}
-
-@media (max-width: 767px) {
- body {
- padding-right: 20px;
- padding-left: 20px;
- }
- .navbar-fixed-top,
- .navbar-fixed-bottom,
- .navbar-static-top {
- margin-right: -20px;
- margin-left: -20px;
- }
- .container-fluid {
- padding: 0;
- }
- .dl-horizontal dt {
- float: none;
- width: auto;
- clear: none;
- text-align: left;
- }
- .dl-horizontal dd {
- margin-left: 0;
- }
- .container {
- width: auto;
- }
- .row-fluid {
- width: 100%;
- }
- .row,
- .thumbnails {
- margin-left: 0;
- }
- .thumbnails > li {
- float: none;
- margin-left: 0;
- }
- [class*="span"],
- .uneditable-input[class*="span"],
- .row-fluid [class*="span"] {
- display: block;
- float: none;
- width: 100%;
- margin-left: 0;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- }
- .span12,
- .row-fluid .span12 {
- width: 100%;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- }
- .row-fluid [class*="offset"]:first-child {
- margin-left: 0;
- }
- .input-large,
- .input-xlarge,
- .input-xxlarge,
- input[class*="span"],
- select[class*="span"],
- textarea[class*="span"],
- .uneditable-input {
- display: block;
- width: 100%;
- min-height: 30px;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- }
- .input-prepend input,
- .input-append input,
- .input-prepend input[class*="span"],
- .input-append input[class*="span"] {
- display: inline-block;
- width: auto;
- }
- .controls-row [class*="span"] + [class*="span"] {
- margin-left: 0;
- }
- .modal {
- position: fixed;
- top: 20px;
- right: 20px;
- left: 20px;
- width: auto;
- margin: 0;
- }
- .modal.fade {
- top: -100px;
- }
- .modal.fade.in {
- top: 20px;
- }
-}
-
-@media (max-width: 480px) {
- .nav-collapse {
- -webkit-transform: translate3d(0, 0, 0);
- }
- .page-header h1 small {
- display: block;
- line-height: 20px;
- }
- input[type="checkbox"],
- input[type="radio"] {
- border: 1px solid #ccc;
- }
- .form-horizontal .control-label {
- float: none;
- width: auto;
- padding-top: 0;
- text-align: left;
- }
- .form-horizontal .controls {
- margin-left: 0;
- }
- .form-horizontal .control-list {
- padding-top: 0;
- }
- .form-horizontal .form-actions {
- padding-right: 10px;
- padding-left: 10px;
- }
- .media .pull-left,
- .media .pull-right {
- display: block;
- float: none;
- margin-bottom: 10px;
- }
- .media-object {
- margin-right: 0;
- margin-left: 0;
- }
- .modal {
- top: 10px;
- right: 10px;
- left: 10px;
- }
- .modal-header .close {
- padding: 10px;
- margin: -10px;
- }
- .carousel-caption {
- position: static;
- }
-}
-
-@media (max-width: 979px) {
- body {
- padding-top: 0;
- }
- .navbar-fixed-top,
- .navbar-fixed-bottom {
- position: static;
- }
- .navbar-fixed-top {
- margin-bottom: 20px;
- }
- .navbar-fixed-bottom {
- margin-top: 20px;
- }
- .navbar-fixed-top .navbar-inner,
- .navbar-fixed-bottom .navbar-inner {
- padding: 5px;
- }
- .navbar .container {
- width: auto;
- padding: 0;
- }
- .navbar .brand {
- padding-right: 10px;
- padding-left: 10px;
- margin: 0 0 0 -5px;
- }
- .nav-collapse {
- clear: both;
- }
- .nav-collapse .nav {
- float: none;
- margin: 0 0 10px;
- }
- .nav-collapse .nav > li {
- float: none;
- }
- .nav-collapse .nav > li > a {
- margin-bottom: 2px;
- }
- .nav-collapse .nav > .divider-vertical {
- display: none;
- }
- .nav-collapse .nav .nav-header {
- color: #777777;
- text-shadow: none;
- }
- .nav-collapse .nav > li > a,
- .nav-collapse .dropdown-menu a {
- padding: 9px 15px;
- font-weight: bold;
- color: #777777;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
- }
- .nav-collapse .btn {
- padding: 4px 10px 4px;
- font-weight: normal;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- }
- .nav-collapse .dropdown-menu li + li a {
- margin-bottom: 2px;
- }
- .nav-collapse .nav > li > a:hover,
- .nav-collapse .nav > li > a:focus,
- .nav-collapse .dropdown-menu a:hover,
- .nav-collapse .dropdown-menu a:focus {
- background-color: #f2f2f2;
- }
- .navbar-inverse .nav-collapse .nav > li > a,
- .navbar-inverse .nav-collapse .dropdown-menu a {
- color: #999999;
- }
- .navbar-inverse .nav-collapse .nav > li > a:hover,
- .navbar-inverse .nav-collapse .nav > li > a:focus,
- .navbar-inverse .nav-collapse .dropdown-menu a:hover,
- .navbar-inverse .nav-collapse .dropdown-menu a:focus {
- background-color: #111111;
- }
- .nav-collapse.in .btn-group {
- padding: 0;
- margin-top: 5px;
- }
- .nav-collapse .dropdown-menu {
- position: static;
- top: auto;
- left: auto;
- display: none;
- float: none;
- max-width: none;
- padding: 0;
- margin: 0 15px;
- background-color: transparent;
- border: none;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
- }
- .nav-collapse .open > .dropdown-menu {
- display: block;
- }
- .nav-collapse .dropdown-menu:before,
- .nav-collapse .dropdown-menu:after {
- display: none;
- }
- .nav-collapse .dropdown-menu .divider {
- display: none;
- }
- .nav-collapse .nav > li > .dropdown-menu:before,
- .nav-collapse .nav > li > .dropdown-menu:after {
- display: none;
- }
- .nav-collapse .navbar-form,
- .nav-collapse .navbar-search {
- float: none;
- padding: 10px 15px;
- margin: 10px 0;
- border-top: 1px solid #f2f2f2;
- border-bottom: 1px solid #f2f2f2;
- -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
- -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
- }
- .navbar-inverse .nav-collapse .navbar-form,
- .navbar-inverse .nav-collapse .navbar-search {
- border-top-color: #111111;
- border-bottom-color: #111111;
- }
- .navbar .nav-collapse .nav.pull-right {
- float: none;
- margin-left: 0;
- }
- .nav-collapse,
- .nav-collapse.collapse {
- height: 0;
- overflow: hidden;
- }
- .navbar .btn-navbar {
- display: block;
- }
- .navbar-static .navbar-inner {
- padding-right: 10px;
- padding-left: 10px;
- }
-}
-
-@media (min-width: 980px) {
- .nav-collapse.collapse {
- height: auto !important;
- overflow: visible !important;
- }
-}
diff --git a/framework/yii/bootstrap/assets/css/bootstrap-responsive.min.css b/framework/yii/bootstrap/assets/css/bootstrap-responsive.min.css
deleted file mode 100644
index f4ede63..0000000
--- a/framework/yii/bootstrap/assets/css/bootstrap-responsive.min.css
+++ /dev/null
@@ -1,9 +0,0 @@
-/*!
- * Bootstrap Responsive v2.3.2
- *
- * Copyright 2012 Twitter, Inc
- * Licensed under the Apache License v2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Designed and built with all the love in the world @twitter by @mdo and @fat.
- */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
diff --git a/framework/yii/bootstrap/assets/css/bootstrap.css b/framework/yii/bootstrap/assets/css/bootstrap.css
deleted file mode 100644
index b725064..0000000
--- a/framework/yii/bootstrap/assets/css/bootstrap.css
+++ /dev/null
@@ -1,6167 +0,0 @@
-/*!
- * Bootstrap v2.3.2
- *
- * Copyright 2012 Twitter, Inc
- * Licensed under the Apache License v2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Designed and built with all the love in the world @twitter by @mdo and @fat.
- */
-
-.clearfix {
- *zoom: 1;
-}
-
-.clearfix:before,
-.clearfix:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.clearfix:after {
- clear: both;
-}
-
-.hide-text {
- font: 0/0 a;
- color: transparent;
- text-shadow: none;
- background-color: transparent;
- border: 0;
-}
-
-.input-block-level {
- display: block;
- width: 100%;
- min-height: 30px;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-
-article,
-aside,
-details,
-figcaption,
-figure,
-footer,
-header,
-hgroup,
-nav,
-section {
- display: block;
-}
-
-audio,
-canvas,
-video {
- display: inline-block;
- *display: inline;
- *zoom: 1;
-}
-
-audio:not([controls]) {
- display: none;
-}
-
-html {
- font-size: 100%;
- -webkit-text-size-adjust: 100%;
- -ms-text-size-adjust: 100%;
-}
-
-a:focus {
- outline: thin dotted #333;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
-}
-
-a:hover,
-a:active {
- outline: 0;
-}
-
-sub,
-sup {
- position: relative;
- font-size: 75%;
- line-height: 0;
- vertical-align: baseline;
-}
-
-sup {
- top: -0.5em;
-}
-
-sub {
- bottom: -0.25em;
-}
-
-img {
- width: auto\9;
- height: auto;
- max-width: 100%;
- vertical-align: middle;
- border: 0;
- -ms-interpolation-mode: bicubic;
-}
-
-#map_canvas img,
-.google-maps img {
- max-width: none;
-}
-
-button,
-input,
-select,
-textarea {
- margin: 0;
- font-size: 100%;
- vertical-align: middle;
-}
-
-button,
-input {
- *overflow: visible;
- line-height: normal;
-}
-
-button::-moz-focus-inner,
-input::-moz-focus-inner {
- padding: 0;
- border: 0;
-}
-
-button,
-html input[type="button"],
-input[type="reset"],
-input[type="submit"] {
- cursor: pointer;
- -webkit-appearance: button;
-}
-
-label,
-select,
-button,
-input[type="button"],
-input[type="reset"],
-input[type="submit"],
-input[type="radio"],
-input[type="checkbox"] {
- cursor: pointer;
-}
-
-input[type="search"] {
- -webkit-box-sizing: content-box;
- -moz-box-sizing: content-box;
- box-sizing: content-box;
- -webkit-appearance: textfield;
-}
-
-input[type="search"]::-webkit-search-decoration,
-input[type="search"]::-webkit-search-cancel-button {
- -webkit-appearance: none;
-}
-
-textarea {
- overflow: auto;
- vertical-align: top;
-}
-
-@media print {
- * {
- color: #000 !important;
- text-shadow: none !important;
- background: transparent !important;
- box-shadow: none !important;
- }
- a,
- a:visited {
- text-decoration: underline;
- }
- a[href]:after {
- content: " (" attr(href) ")";
- }
- abbr[title]:after {
- content: " (" attr(title) ")";
- }
- .ir a:after,
- a[href^="javascript:"]:after,
- a[href^="#"]:after {
- content: "";
- }
- pre,
- blockquote {
- border: 1px solid #999;
- page-break-inside: avoid;
- }
- thead {
- display: table-header-group;
- }
- tr,
- img {
- page-break-inside: avoid;
- }
- img {
- max-width: 100% !important;
- }
- @page {
- margin: 0.5cm;
- }
- p,
- h2,
- h3 {
- orphans: 3;
- widows: 3;
- }
- h2,
- h3 {
- page-break-after: avoid;
- }
-}
-
-body {
- margin: 0;
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-size: 14px;
- line-height: 20px;
- color: #333333;
- background-color: #ffffff;
-}
-
-a {
- color: #0088cc;
- text-decoration: none;
-}
-
-a:hover,
-a:focus {
- color: #005580;
- text-decoration: underline;
-}
-
-.img-rounded {
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
-}
-
-.img-polaroid {
- padding: 4px;
- background-color: #fff;
- border: 1px solid #ccc;
- border: 1px solid rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-}
-
-.img-circle {
- -webkit-border-radius: 500px;
- -moz-border-radius: 500px;
- border-radius: 500px;
-}
-
-.row {
- margin-left: -20px;
- *zoom: 1;
-}
-
-.row:before,
-.row:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.row:after {
- clear: both;
-}
-
-[class*="span"] {
- float: left;
- min-height: 1px;
- margin-left: 20px;
-}
-
-.container,
-.navbar-static-top .container,
-.navbar-fixed-top .container,
-.navbar-fixed-bottom .container {
- width: 940px;
-}
-
-.span12 {
- width: 940px;
-}
-
-.span11 {
- width: 860px;
-}
-
-.span10 {
- width: 780px;
-}
-
-.span9 {
- width: 700px;
-}
-
-.span8 {
- width: 620px;
-}
-
-.span7 {
- width: 540px;
-}
-
-.span6 {
- width: 460px;
-}
-
-.span5 {
- width: 380px;
-}
-
-.span4 {
- width: 300px;
-}
-
-.span3 {
- width: 220px;
-}
-
-.span2 {
- width: 140px;
-}
-
-.span1 {
- width: 60px;
-}
-
-.offset12 {
- margin-left: 980px;
-}
-
-.offset11 {
- margin-left: 900px;
-}
-
-.offset10 {
- margin-left: 820px;
-}
-
-.offset9 {
- margin-left: 740px;
-}
-
-.offset8 {
- margin-left: 660px;
-}
-
-.offset7 {
- margin-left: 580px;
-}
-
-.offset6 {
- margin-left: 500px;
-}
-
-.offset5 {
- margin-left: 420px;
-}
-
-.offset4 {
- margin-left: 340px;
-}
-
-.offset3 {
- margin-left: 260px;
-}
-
-.offset2 {
- margin-left: 180px;
-}
-
-.offset1 {
- margin-left: 100px;
-}
-
-.row-fluid {
- width: 100%;
- *zoom: 1;
-}
-
-.row-fluid:before,
-.row-fluid:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.row-fluid:after {
- clear: both;
-}
-
-.row-fluid [class*="span"] {
- display: block;
- float: left;
- width: 100%;
- min-height: 30px;
- margin-left: 2.127659574468085%;
- *margin-left: 2.074468085106383%;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-
-.row-fluid [class*="span"]:first-child {
- margin-left: 0;
-}
-
-.row-fluid .controls-row [class*="span"] + [class*="span"] {
- margin-left: 2.127659574468085%;
-}
-
-.row-fluid .span12 {
- width: 100%;
- *width: 99.94680851063829%;
-}
-
-.row-fluid .span11 {
- width: 91.48936170212765%;
- *width: 91.43617021276594%;
-}
-
-.row-fluid .span10 {
- width: 82.97872340425532%;
- *width: 82.92553191489361%;
-}
-
-.row-fluid .span9 {
- width: 74.46808510638297%;
- *width: 74.41489361702126%;
-}
-
-.row-fluid .span8 {
- width: 65.95744680851064%;
- *width: 65.90425531914893%;
-}
-
-.row-fluid .span7 {
- width: 57.44680851063829%;
- *width: 57.39361702127659%;
-}
-
-.row-fluid .span6 {
- width: 48.93617021276595%;
- *width: 48.88297872340425%;
-}
-
-.row-fluid .span5 {
- width: 40.42553191489362%;
- *width: 40.37234042553192%;
-}
-
-.row-fluid .span4 {
- width: 31.914893617021278%;
- *width: 31.861702127659576%;
-}
-
-.row-fluid .span3 {
- width: 23.404255319148934%;
- *width: 23.351063829787233%;
-}
-
-.row-fluid .span2 {
- width: 14.893617021276595%;
- *width: 14.840425531914894%;
-}
-
-.row-fluid .span1 {
- width: 6.382978723404255%;
- *width: 6.329787234042553%;
-}
-
-.row-fluid .offset12 {
- margin-left: 104.25531914893617%;
- *margin-left: 104.14893617021275%;
-}
-
-.row-fluid .offset12:first-child {
- margin-left: 102.12765957446808%;
- *margin-left: 102.02127659574467%;
-}
-
-.row-fluid .offset11 {
- margin-left: 95.74468085106382%;
- *margin-left: 95.6382978723404%;
-}
-
-.row-fluid .offset11:first-child {
- margin-left: 93.61702127659574%;
- *margin-left: 93.51063829787232%;
-}
-
-.row-fluid .offset10 {
- margin-left: 87.23404255319149%;
- *margin-left: 87.12765957446807%;
-}
-
-.row-fluid .offset10:first-child {
- margin-left: 85.1063829787234%;
- *margin-left: 84.99999999999999%;
-}
-
-.row-fluid .offset9 {
- margin-left: 78.72340425531914%;
- *margin-left: 78.61702127659572%;
-}
-
-.row-fluid .offset9:first-child {
- margin-left: 76.59574468085106%;
- *margin-left: 76.48936170212764%;
-}
-
-.row-fluid .offset8 {
- margin-left: 70.2127659574468%;
- *margin-left: 70.10638297872339%;
-}
-
-.row-fluid .offset8:first-child {
- margin-left: 68.08510638297872%;
- *margin-left: 67.9787234042553%;
-}
-
-.row-fluid .offset7 {
- margin-left: 61.70212765957446%;
- *margin-left: 61.59574468085106%;
-}
-
-.row-fluid .offset7:first-child {
- margin-left: 59.574468085106375%;
- *margin-left: 59.46808510638297%;
-}
-
-.row-fluid .offset6 {
- margin-left: 53.191489361702125%;
- *margin-left: 53.085106382978715%;
-}
-
-.row-fluid .offset6:first-child {
- margin-left: 51.063829787234035%;
- *margin-left: 50.95744680851063%;
-}
-
-.row-fluid .offset5 {
- margin-left: 44.68085106382979%;
- *margin-left: 44.57446808510638%;
-}
-
-.row-fluid .offset5:first-child {
- margin-left: 42.5531914893617%;
- *margin-left: 42.4468085106383%;
-}
-
-.row-fluid .offset4 {
- margin-left: 36.170212765957444%;
- *margin-left: 36.06382978723405%;
-}
-
-.row-fluid .offset4:first-child {
- margin-left: 34.04255319148936%;
- *margin-left: 33.93617021276596%;
-}
-
-.row-fluid .offset3 {
- margin-left: 27.659574468085104%;
- *margin-left: 27.5531914893617%;
-}
-
-.row-fluid .offset3:first-child {
- margin-left: 25.53191489361702%;
- *margin-left: 25.425531914893618%;
-}
-
-.row-fluid .offset2 {
- margin-left: 19.148936170212764%;
- *margin-left: 19.04255319148936%;
-}
-
-.row-fluid .offset2:first-child {
- margin-left: 17.02127659574468%;
- *margin-left: 16.914893617021278%;
-}
-
-.row-fluid .offset1 {
- margin-left: 10.638297872340425%;
- *margin-left: 10.53191489361702%;
-}
-
-.row-fluid .offset1:first-child {
- margin-left: 8.51063829787234%;
- *margin-left: 8.404255319148938%;
-}
-
-[class*="span"].hide,
-.row-fluid [class*="span"].hide {
- display: none;
-}
-
-[class*="span"].pull-right,
-.row-fluid [class*="span"].pull-right {
- float: right;
-}
-
-.container {
- margin-right: auto;
- margin-left: auto;
- *zoom: 1;
-}
-
-.container:before,
-.container:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.container:after {
- clear: both;
-}
-
-.container-fluid {
- padding-right: 20px;
- padding-left: 20px;
- *zoom: 1;
-}
-
-.container-fluid:before,
-.container-fluid:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.container-fluid:after {
- clear: both;
-}
-
-p {
- margin: 0 0 10px;
-}
-
-.lead {
- margin-bottom: 20px;
- font-size: 21px;
- font-weight: 200;
- line-height: 30px;
-}
-
-small {
- font-size: 85%;
-}
-
-strong {
- font-weight: bold;
-}
-
-em {
- font-style: italic;
-}
-
-cite {
- font-style: normal;
-}
-
-.muted {
- color: #999999;
-}
-
-a.muted:hover,
-a.muted:focus {
- color: #808080;
-}
-
-.text-warning {
- color: #c09853;
-}
-
-a.text-warning:hover,
-a.text-warning:focus {
- color: #a47e3c;
-}
-
-.text-error {
- color: #b94a48;
-}
-
-a.text-error:hover,
-a.text-error:focus {
- color: #953b39;
-}
-
-.text-info {
- color: #3a87ad;
-}
-
-a.text-info:hover,
-a.text-info:focus {
- color: #2d6987;
-}
-
-.text-success {
- color: #468847;
-}
-
-a.text-success:hover,
-a.text-success:focus {
- color: #356635;
-}
-
-.text-left {
- text-align: left;
-}
-
-.text-right {
- text-align: right;
-}
-
-.text-center {
- text-align: center;
-}
-
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- margin: 10px 0;
- font-family: inherit;
- font-weight: bold;
- line-height: 20px;
- color: inherit;
- text-rendering: optimizelegibility;
-}
-
-h1 small,
-h2 small,
-h3 small,
-h4 small,
-h5 small,
-h6 small {
- font-weight: normal;
- line-height: 1;
- color: #999999;
-}
-
-h1,
-h2,
-h3 {
- line-height: 40px;
-}
-
-h1 {
- font-size: 38.5px;
-}
-
-h2 {
- font-size: 31.5px;
-}
-
-h3 {
- font-size: 24.5px;
-}
-
-h4 {
- font-size: 17.5px;
-}
-
-h5 {
- font-size: 14px;
-}
-
-h6 {
- font-size: 11.9px;
-}
-
-h1 small {
- font-size: 24.5px;
-}
-
-h2 small {
- font-size: 17.5px;
-}
-
-h3 small {
- font-size: 14px;
-}
-
-h4 small {
- font-size: 14px;
-}
-
-.page-header {
- padding-bottom: 9px;
- margin: 20px 0 30px;
- border-bottom: 1px solid #eeeeee;
-}
-
-ul,
-ol {
- padding: 0;
- margin: 0 0 10px 25px;
-}
-
-ul ul,
-ul ol,
-ol ol,
-ol ul {
- margin-bottom: 0;
-}
-
-li {
- line-height: 20px;
-}
-
-ul.unstyled,
-ol.unstyled {
- margin-left: 0;
- list-style: none;
-}
-
-ul.inline,
-ol.inline {
- margin-left: 0;
- list-style: none;
-}
-
-ul.inline > li,
-ol.inline > li {
- display: inline-block;
- *display: inline;
- padding-right: 5px;
- padding-left: 5px;
- *zoom: 1;
-}
-
-dl {
- margin-bottom: 20px;
-}
-
-dt,
-dd {
- line-height: 20px;
-}
-
-dt {
- font-weight: bold;
-}
-
-dd {
- margin-left: 10px;
-}
-
-.dl-horizontal {
- *zoom: 1;
-}
-
-.dl-horizontal:before,
-.dl-horizontal:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.dl-horizontal:after {
- clear: both;
-}
-
-.dl-horizontal dt {
- float: left;
- width: 160px;
- overflow: hidden;
- clear: left;
- text-align: right;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.dl-horizontal dd {
- margin-left: 180px;
-}
-
-hr {
- margin: 20px 0;
- border: 0;
- border-top: 1px solid #eeeeee;
- border-bottom: 1px solid #ffffff;
-}
-
-abbr[title],
-abbr[data-original-title] {
- cursor: help;
- border-bottom: 1px dotted #999999;
-}
-
-abbr.initialism {
- font-size: 90%;
- text-transform: uppercase;
-}
-
-blockquote {
- padding: 0 0 0 15px;
- margin: 0 0 20px;
- border-left: 5px solid #eeeeee;
-}
-
-blockquote p {
- margin-bottom: 0;
- font-size: 17.5px;
- font-weight: 300;
- line-height: 1.25;
-}
-
-blockquote small {
- display: block;
- line-height: 20px;
- color: #999999;
-}
-
-blockquote small:before {
- content: '\2014 \00A0';
-}
-
-blockquote.pull-right {
- float: right;
- padding-right: 15px;
- padding-left: 0;
- border-right: 5px solid #eeeeee;
- border-left: 0;
-}
-
-blockquote.pull-right p,
-blockquote.pull-right small {
- text-align: right;
-}
-
-blockquote.pull-right small:before {
- content: '';
-}
-
-blockquote.pull-right small:after {
- content: '\00A0 \2014';
-}
-
-q:before,
-q:after,
-blockquote:before,
-blockquote:after {
- content: "";
-}
-
-address {
- display: block;
- margin-bottom: 20px;
- font-style: normal;
- line-height: 20px;
-}
-
-code,
-pre {
- padding: 0 3px 2px;
- font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
- font-size: 12px;
- color: #333333;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-
-code {
- padding: 2px 4px;
- color: #d14;
- white-space: nowrap;
- background-color: #f7f7f9;
- border: 1px solid #e1e1e8;
-}
-
-pre {
- display: block;
- padding: 9.5px;
- margin: 0 0 10px;
- font-size: 13px;
- line-height: 20px;
- word-break: break-all;
- word-wrap: break-word;
- white-space: pre;
- white-space: pre-wrap;
- background-color: #f5f5f5;
- border: 1px solid #ccc;
- border: 1px solid rgba(0, 0, 0, 0.15);
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-
-pre.prettyprint {
- margin-bottom: 20px;
-}
-
-pre code {
- padding: 0;
- color: inherit;
- white-space: pre;
- white-space: pre-wrap;
- background-color: transparent;
- border: 0;
-}
-
-.pre-scrollable {
- max-height: 340px;
- overflow-y: scroll;
-}
-
-form {
- margin: 0 0 20px;
-}
-
-fieldset {
- padding: 0;
- margin: 0;
- border: 0;
-}
-
-legend {
- display: block;
- width: 100%;
- padding: 0;
- margin-bottom: 20px;
- font-size: 21px;
- line-height: 40px;
- color: #333333;
- border: 0;
- border-bottom: 1px solid #e5e5e5;
-}
-
-legend small {
- font-size: 15px;
- color: #999999;
-}
-
-label,
-input,
-button,
-select,
-textarea {
- font-size: 14px;
- font-weight: normal;
- line-height: 20px;
-}
-
-input,
-button,
-select,
-textarea {
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
-}
-
-label {
- display: block;
- margin-bottom: 5px;
-}
-
-select,
-textarea,
-input[type="text"],
-input[type="password"],
-input[type="datetime"],
-input[type="datetime-local"],
-input[type="date"],
-input[type="month"],
-input[type="time"],
-input[type="week"],
-input[type="number"],
-input[type="email"],
-input[type="url"],
-input[type="search"],
-input[type="tel"],
-input[type="color"],
-.uneditable-input {
- display: inline-block;
- height: 20px;
- padding: 4px 6px;
- margin-bottom: 10px;
- font-size: 14px;
- line-height: 20px;
- color: #555555;
- vertical-align: middle;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-
-input,
-textarea,
-.uneditable-input {
- width: 206px;
-}
-
-textarea {
- height: auto;
-}
-
-textarea,
-input[type="text"],
-input[type="password"],
-input[type="datetime"],
-input[type="datetime-local"],
-input[type="date"],
-input[type="month"],
-input[type="time"],
-input[type="week"],
-input[type="number"],
-input[type="email"],
-input[type="url"],
-input[type="search"],
-input[type="tel"],
-input[type="color"],
-.uneditable-input {
- background-color: #ffffff;
- border: 1px solid #cccccc;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
- -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
- -o-transition: border linear 0.2s, box-shadow linear 0.2s;
- transition: border linear 0.2s, box-shadow linear 0.2s;
-}
-
-textarea:focus,
-input[type="text"]:focus,
-input[type="password"]:focus,
-input[type="datetime"]:focus,
-input[type="datetime-local"]:focus,
-input[type="date"]:focus,
-input[type="month"]:focus,
-input[type="time"]:focus,
-input[type="week"]:focus,
-input[type="number"]:focus,
-input[type="email"]:focus,
-input[type="url"]:focus,
-input[type="search"]:focus,
-input[type="tel"]:focus,
-input[type="color"]:focus,
-.uneditable-input:focus {
- border-color: rgba(82, 168, 236, 0.8);
- outline: 0;
- outline: thin dotted \9;
- /* IE6-9 */
-
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
-}
-
-input[type="radio"],
-input[type="checkbox"] {
- margin: 4px 0 0;
- margin-top: 1px \9;
- *margin-top: 0;
- line-height: normal;
-}
-
-input[type="file"],
-input[type="image"],
-input[type="submit"],
-input[type="reset"],
-input[type="button"],
-input[type="radio"],
-input[type="checkbox"] {
- width: auto;
-}
-
-select,
-input[type="file"] {
- height: 30px;
- /* In IE7, the height of the select element cannot be changed by height, only font-size */
-
- *margin-top: 4px;
- /* For IE7, add top margin to align select with labels */
-
- line-height: 30px;
-}
-
-select {
- width: 220px;
- background-color: #ffffff;
- border: 1px solid #cccccc;
-}
-
-select[multiple],
-select[size] {
- height: auto;
-}
-
-select:focus,
-input[type="file"]:focus,
-input[type="radio"]:focus,
-input[type="checkbox"]:focus {
- outline: thin dotted #333;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
-}
-
-.uneditable-input,
-.uneditable-textarea {
- color: #999999;
- cursor: not-allowed;
- background-color: #fcfcfc;
- border-color: #cccccc;
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
- -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
- box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
-}
-
-.uneditable-input {
- overflow: hidden;
- white-space: nowrap;
-}
-
-.uneditable-textarea {
- width: auto;
- height: auto;
-}
-
-input:-moz-placeholder,
-textarea:-moz-placeholder {
- color: #999999;
-}
-
-input:-ms-input-placeholder,
-textarea:-ms-input-placeholder {
- color: #999999;
-}
-
-input::-webkit-input-placeholder,
-textarea::-webkit-input-placeholder {
- color: #999999;
-}
-
-.radio,
-.checkbox {
- min-height: 20px;
- padding-left: 20px;
-}
-
-.radio input[type="radio"],
-.checkbox input[type="checkbox"] {
- float: left;
- margin-left: -20px;
-}
-
-.controls > .radio:first-child,
-.controls > .checkbox:first-child {
- padding-top: 5px;
-}
-
-.radio.inline,
-.checkbox.inline {
- display: inline-block;
- padding-top: 5px;
- margin-bottom: 0;
- vertical-align: middle;
-}
-
-.radio.inline + .radio.inline,
-.checkbox.inline + .checkbox.inline {
- margin-left: 10px;
-}
-
-.input-mini {
- width: 60px;
-}
-
-.input-small {
- width: 90px;
-}
-
-.input-medium {
- width: 150px;
-}
-
-.input-large {
- width: 210px;
-}
-
-.input-xlarge {
- width: 270px;
-}
-
-.input-xxlarge {
- width: 530px;
-}
-
-input[class*="span"],
-select[class*="span"],
-textarea[class*="span"],
-.uneditable-input[class*="span"],
-.row-fluid input[class*="span"],
-.row-fluid select[class*="span"],
-.row-fluid textarea[class*="span"],
-.row-fluid .uneditable-input[class*="span"] {
- float: none;
- margin-left: 0;
-}
-
-.input-append input[class*="span"],
-.input-append .uneditable-input[class*="span"],
-.input-prepend input[class*="span"],
-.input-prepend .uneditable-input[class*="span"],
-.row-fluid input[class*="span"],
-.row-fluid select[class*="span"],
-.row-fluid textarea[class*="span"],
-.row-fluid .uneditable-input[class*="span"],
-.row-fluid .input-prepend [class*="span"],
-.row-fluid .input-append [class*="span"] {
- display: inline-block;
-}
-
-input,
-textarea,
-.uneditable-input {
- margin-left: 0;
-}
-
-.controls-row [class*="span"] + [class*="span"] {
- margin-left: 20px;
-}
-
-input.span12,
-textarea.span12,
-.uneditable-input.span12 {
- width: 926px;
-}
-
-input.span11,
-textarea.span11,
-.uneditable-input.span11 {
- width: 846px;
-}
-
-input.span10,
-textarea.span10,
-.uneditable-input.span10 {
- width: 766px;
-}
-
-input.span9,
-textarea.span9,
-.uneditable-input.span9 {
- width: 686px;
-}
-
-input.span8,
-textarea.span8,
-.uneditable-input.span8 {
- width: 606px;
-}
-
-input.span7,
-textarea.span7,
-.uneditable-input.span7 {
- width: 526px;
-}
-
-input.span6,
-textarea.span6,
-.uneditable-input.span6 {
- width: 446px;
-}
-
-input.span5,
-textarea.span5,
-.uneditable-input.span5 {
- width: 366px;
-}
-
-input.span4,
-textarea.span4,
-.uneditable-input.span4 {
- width: 286px;
-}
-
-input.span3,
-textarea.span3,
-.uneditable-input.span3 {
- width: 206px;
-}
-
-input.span2,
-textarea.span2,
-.uneditable-input.span2 {
- width: 126px;
-}
-
-input.span1,
-textarea.span1,
-.uneditable-input.span1 {
- width: 46px;
-}
-
-.controls-row {
- *zoom: 1;
-}
-
-.controls-row:before,
-.controls-row:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.controls-row:after {
- clear: both;
-}
-
-.controls-row [class*="span"],
-.row-fluid .controls-row [class*="span"] {
- float: left;
-}
-
-.controls-row .checkbox[class*="span"],
-.controls-row .radio[class*="span"] {
- padding-top: 5px;
-}
-
-input[disabled],
-select[disabled],
-textarea[disabled],
-input[readonly],
-select[readonly],
-textarea[readonly] {
- cursor: not-allowed;
- background-color: #eeeeee;
-}
-
-input[type="radio"][disabled],
-input[type="checkbox"][disabled],
-input[type="radio"][readonly],
-input[type="checkbox"][readonly] {
- background-color: transparent;
-}
-
-.control-group.warning .control-label,
-.control-group.warning .help-block,
-.control-group.warning .help-inline {
- color: #c09853;
-}
-
-.control-group.warning .checkbox,
-.control-group.warning .radio,
-.control-group.warning input,
-.control-group.warning select,
-.control-group.warning textarea {
- color: #c09853;
-}
-
-.control-group.warning input,
-.control-group.warning select,
-.control-group.warning textarea {
- border-color: #c09853;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-}
-
-.control-group.warning input:focus,
-.control-group.warning select:focus,
-.control-group.warning textarea:focus {
- border-color: #a47e3c;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
-}
-
-.control-group.warning .input-prepend .add-on,
-.control-group.warning .input-append .add-on {
- color: #c09853;
- background-color: #fcf8e3;
- border-color: #c09853;
-}
-
-.control-group.error .control-label,
-.control-group.error .help-block,
-.control-group.error .help-inline {
- color: #b94a48;
-}
-
-.control-group.error .checkbox,
-.control-group.error .radio,
-.control-group.error input,
-.control-group.error select,
-.control-group.error textarea {
- color: #b94a48;
-}
-
-.control-group.error input,
-.control-group.error select,
-.control-group.error textarea {
- border-color: #b94a48;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-}
-
-.control-group.error input:focus,
-.control-group.error select:focus,
-.control-group.error textarea:focus {
- border-color: #953b39;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
-}
-
-.control-group.error .input-prepend .add-on,
-.control-group.error .input-append .add-on {
- color: #b94a48;
- background-color: #f2dede;
- border-color: #b94a48;
-}
-
-.control-group.success .control-label,
-.control-group.success .help-block,
-.control-group.success .help-inline {
- color: #468847;
-}
-
-.control-group.success .checkbox,
-.control-group.success .radio,
-.control-group.success input,
-.control-group.success select,
-.control-group.success textarea {
- color: #468847;
-}
-
-.control-group.success input,
-.control-group.success select,
-.control-group.success textarea {
- border-color: #468847;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-}
-
-.control-group.success input:focus,
-.control-group.success select:focus,
-.control-group.success textarea:focus {
- border-color: #356635;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
-}
-
-.control-group.success .input-prepend .add-on,
-.control-group.success .input-append .add-on {
- color: #468847;
- background-color: #dff0d8;
- border-color: #468847;
-}
-
-.control-group.info .control-label,
-.control-group.info .help-block,
-.control-group.info .help-inline {
- color: #3a87ad;
-}
-
-.control-group.info .checkbox,
-.control-group.info .radio,
-.control-group.info input,
-.control-group.info select,
-.control-group.info textarea {
- color: #3a87ad;
-}
-
-.control-group.info input,
-.control-group.info select,
-.control-group.info textarea {
- border-color: #3a87ad;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-}
-
-.control-group.info input:focus,
-.control-group.info select:focus,
-.control-group.info textarea:focus {
- border-color: #2d6987;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
-}
-
-.control-group.info .input-prepend .add-on,
-.control-group.info .input-append .add-on {
- color: #3a87ad;
- background-color: #d9edf7;
- border-color: #3a87ad;
-}
-
-input:focus:invalid,
-textarea:focus:invalid,
-select:focus:invalid {
- color: #b94a48;
- border-color: #ee5f5b;
-}
-
-input:focus:invalid:focus,
-textarea:focus:invalid:focus,
-select:focus:invalid:focus {
- border-color: #e9322d;
- -webkit-box-shadow: 0 0 6px #f8b9b7;
- -moz-box-shadow: 0 0 6px #f8b9b7;
- box-shadow: 0 0 6px #f8b9b7;
-}
-
-.form-actions {
- padding: 19px 20px 20px;
- margin-top: 20px;
- margin-bottom: 20px;
- background-color: #f5f5f5;
- border-top: 1px solid #e5e5e5;
- *zoom: 1;
-}
-
-.form-actions:before,
-.form-actions:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.form-actions:after {
- clear: both;
-}
-
-.help-block,
-.help-inline {
- color: #595959;
-}
-
-.help-block {
- display: block;
- margin-bottom: 10px;
-}
-
-.help-inline {
- display: inline-block;
- *display: inline;
- padding-left: 5px;
- vertical-align: middle;
- *zoom: 1;
-}
-
-.input-append,
-.input-prepend {
- display: inline-block;
- margin-bottom: 10px;
- font-size: 0;
- white-space: nowrap;
- vertical-align: middle;
-}
-
-.input-append input,
-.input-prepend input,
-.input-append select,
-.input-prepend select,
-.input-append .uneditable-input,
-.input-prepend .uneditable-input,
-.input-append .dropdown-menu,
-.input-prepend .dropdown-menu,
-.input-append .popover,
-.input-prepend .popover {
- font-size: 14px;
-}
-
-.input-append input,
-.input-prepend input,
-.input-append select,
-.input-prepend select,
-.input-append .uneditable-input,
-.input-prepend .uneditable-input {
- position: relative;
- margin-bottom: 0;
- *margin-left: 0;
- vertical-align: top;
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
-}
-
-.input-append input:focus,
-.input-prepend input:focus,
-.input-append select:focus,
-.input-prepend select:focus,
-.input-append .uneditable-input:focus,
-.input-prepend .uneditable-input:focus {
- z-index: 2;
-}
-
-.input-append .add-on,
-.input-prepend .add-on {
- display: inline-block;
- width: auto;
- height: 20px;
- min-width: 16px;
- padding: 4px 5px;
- font-size: 14px;
- font-weight: normal;
- line-height: 20px;
- text-align: center;
- text-shadow: 0 1px 0 #ffffff;
- background-color: #eeeeee;
- border: 1px solid #ccc;
-}
-
-.input-append .add-on,
-.input-prepend .add-on,
-.input-append .btn,
-.input-prepend .btn,
-.input-append .btn-group > .dropdown-toggle,
-.input-prepend .btn-group > .dropdown-toggle {
- vertical-align: top;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
-}
-
-.input-append .active,
-.input-prepend .active {
- background-color: #a9dba9;
- border-color: #46a546;
-}
-
-.input-prepend .add-on,
-.input-prepend .btn {
- margin-right: -1px;
-}
-
-.input-prepend .add-on:first-child,
-.input-prepend .btn:first-child {
- -webkit-border-radius: 4px 0 0 4px;
- -moz-border-radius: 4px 0 0 4px;
- border-radius: 4px 0 0 4px;
-}
-
-.input-append input,
-.input-append select,
-.input-append .uneditable-input {
- -webkit-border-radius: 4px 0 0 4px;
- -moz-border-radius: 4px 0 0 4px;
- border-radius: 4px 0 0 4px;
-}
-
-.input-append input + .btn-group .btn:last-child,
-.input-append select + .btn-group .btn:last-child,
-.input-append .uneditable-input + .btn-group .btn:last-child {
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
-}
-
-.input-append .add-on,
-.input-append .btn,
-.input-append .btn-group {
- margin-left: -1px;
-}
-
-.input-append .add-on:last-child,
-.input-append .btn:last-child,
-.input-append .btn-group:last-child > .dropdown-toggle {
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
-}
-
-.input-prepend.input-append input,
-.input-prepend.input-append select,
-.input-prepend.input-append .uneditable-input {
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
-}
-
-.input-prepend.input-append input + .btn-group .btn,
-.input-prepend.input-append select + .btn-group .btn,
-.input-prepend.input-append .uneditable-input + .btn-group .btn {
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
-}
-
-.input-prepend.input-append .add-on:first-child,
-.input-prepend.input-append .btn:first-child {
- margin-right: -1px;
- -webkit-border-radius: 4px 0 0 4px;
- -moz-border-radius: 4px 0 0 4px;
- border-radius: 4px 0 0 4px;
-}
-
-.input-prepend.input-append .add-on:last-child,
-.input-prepend.input-append .btn:last-child {
- margin-left: -1px;
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
-}
-
-.input-prepend.input-append .btn-group:first-child {
- margin-left: 0;
-}
-
-input.search-query {
- padding-right: 14px;
- padding-right: 4px \9;
- padding-left: 14px;
- padding-left: 4px \9;
- /* IE7-8 doesn't have border-radius, so don't indent the padding */
-
- margin-bottom: 0;
- -webkit-border-radius: 15px;
- -moz-border-radius: 15px;
- border-radius: 15px;
-}
-
-/* Allow for input prepend/append in search forms */
-
-.form-search .input-append .search-query,
-.form-search .input-prepend .search-query {
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
-}
-
-.form-search .input-append .search-query {
- -webkit-border-radius: 14px 0 0 14px;
- -moz-border-radius: 14px 0 0 14px;
- border-radius: 14px 0 0 14px;
-}
-
-.form-search .input-append .btn {
- -webkit-border-radius: 0 14px 14px 0;
- -moz-border-radius: 0 14px 14px 0;
- border-radius: 0 14px 14px 0;
-}
-
-.form-search .input-prepend .search-query {
- -webkit-border-radius: 0 14px 14px 0;
- -moz-border-radius: 0 14px 14px 0;
- border-radius: 0 14px 14px 0;
-}
-
-.form-search .input-prepend .btn {
- -webkit-border-radius: 14px 0 0 14px;
- -moz-border-radius: 14px 0 0 14px;
- border-radius: 14px 0 0 14px;
-}
-
-.form-search input,
-.form-inline input,
-.form-horizontal input,
-.form-search textarea,
-.form-inline textarea,
-.form-horizontal textarea,
-.form-search select,
-.form-inline select,
-.form-horizontal select,
-.form-search .help-inline,
-.form-inline .help-inline,
-.form-horizontal .help-inline,
-.form-search .uneditable-input,
-.form-inline .uneditable-input,
-.form-horizontal .uneditable-input,
-.form-search .input-prepend,
-.form-inline .input-prepend,
-.form-horizontal .input-prepend,
-.form-search .input-append,
-.form-inline .input-append,
-.form-horizontal .input-append {
- display: inline-block;
- *display: inline;
- margin-bottom: 0;
- vertical-align: middle;
- *zoom: 1;
-}
-
-.form-search .hide,
-.form-inline .hide,
-.form-horizontal .hide {
- display: none;
-}
-
-.form-search label,
-.form-inline label,
-.form-search .btn-group,
-.form-inline .btn-group {
- display: inline-block;
-}
-
-.form-search .input-append,
-.form-inline .input-append,
-.form-search .input-prepend,
-.form-inline .input-prepend {
- margin-bottom: 0;
-}
-
-.form-search .radio,
-.form-search .checkbox,
-.form-inline .radio,
-.form-inline .checkbox {
- padding-left: 0;
- margin-bottom: 0;
- vertical-align: middle;
-}
-
-.form-search .radio input[type="radio"],
-.form-search .checkbox input[type="checkbox"],
-.form-inline .radio input[type="radio"],
-.form-inline .checkbox input[type="checkbox"] {
- float: left;
- margin-right: 3px;
- margin-left: 0;
-}
-
-.control-group {
- margin-bottom: 10px;
-}
-
-legend + .control-group {
- margin-top: 20px;
- -webkit-margin-top-collapse: separate;
-}
-
-.form-horizontal .control-group {
- margin-bottom: 20px;
- *zoom: 1;
-}
-
-.form-horizontal .control-group:before,
-.form-horizontal .control-group:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.form-horizontal .control-group:after {
- clear: both;
-}
-
-.form-horizontal .control-label {
- float: left;
- width: 160px;
- padding-top: 5px;
- text-align: right;
-}
-
-.form-horizontal .controls {
- *display: inline-block;
- *padding-left: 20px;
- margin-left: 180px;
- *margin-left: 0;
-}
-
-.form-horizontal .controls:first-child {
- *padding-left: 180px;
-}
-
-.form-horizontal .help-block {
- margin-bottom: 0;
-}
-
-.form-horizontal input + .help-block,
-.form-horizontal select + .help-block,
-.form-horizontal textarea + .help-block,
-.form-horizontal .uneditable-input + .help-block,
-.form-horizontal .input-prepend + .help-block,
-.form-horizontal .input-append + .help-block {
- margin-top: 10px;
-}
-
-.form-horizontal .form-actions {
- padding-left: 180px;
-}
-
-table {
- max-width: 100%;
- background-color: transparent;
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-.table {
- width: 100%;
- margin-bottom: 20px;
-}
-
-.table th,
-.table td {
- padding: 8px;
- line-height: 20px;
- text-align: left;
- vertical-align: top;
- border-top: 1px solid #dddddd;
-}
-
-.table th {
- font-weight: bold;
-}
-
-.table thead th {
- vertical-align: bottom;
-}
-
-.table caption + thead tr:first-child th,
-.table caption + thead tr:first-child td,
-.table colgroup + thead tr:first-child th,
-.table colgroup + thead tr:first-child td,
-.table thead:first-child tr:first-child th,
-.table thead:first-child tr:first-child td {
- border-top: 0;
-}
-
-.table tbody + tbody {
- border-top: 2px solid #dddddd;
-}
-
-.table .table {
- background-color: #ffffff;
-}
-
-.table-condensed th,
-.table-condensed td {
- padding: 4px 5px;
-}
-
-.table-bordered {
- border: 1px solid #dddddd;
- border-collapse: separate;
- *border-collapse: collapse;
- border-left: 0;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-
-.table-bordered th,
-.table-bordered td {
- border-left: 1px solid #dddddd;
-}
-
-.table-bordered caption + thead tr:first-child th,
-.table-bordered caption + tbody tr:first-child th,
-.table-bordered caption + tbody tr:first-child td,
-.table-bordered colgroup + thead tr:first-child th,
-.table-bordered colgroup + tbody tr:first-child th,
-.table-bordered colgroup + tbody tr:first-child td,
-.table-bordered thead:first-child tr:first-child th,
-.table-bordered tbody:first-child tr:first-child th,
-.table-bordered tbody:first-child tr:first-child td {
- border-top: 0;
-}
-
-.table-bordered thead:first-child tr:first-child > th:first-child,
-.table-bordered tbody:first-child tr:first-child > td:first-child,
-.table-bordered tbody:first-child tr:first-child > th:first-child {
- -webkit-border-top-left-radius: 4px;
- border-top-left-radius: 4px;
- -moz-border-radius-topleft: 4px;
-}
-
-.table-bordered thead:first-child tr:first-child > th:last-child,
-.table-bordered tbody:first-child tr:first-child > td:last-child,
-.table-bordered tbody:first-child tr:first-child > th:last-child {
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
- -moz-border-radius-topright: 4px;
-}
-
-.table-bordered thead:last-child tr:last-child > th:first-child,
-.table-bordered tbody:last-child tr:last-child > td:first-child,
-.table-bordered tbody:last-child tr:last-child > th:first-child,
-.table-bordered tfoot:last-child tr:last-child > td:first-child,
-.table-bordered tfoot:last-child tr:last-child > th:first-child {
- -webkit-border-bottom-left-radius: 4px;
- border-bottom-left-radius: 4px;
- -moz-border-radius-bottomleft: 4px;
-}
-
-.table-bordered thead:last-child tr:last-child > th:last-child,
-.table-bordered tbody:last-child tr:last-child > td:last-child,
-.table-bordered tbody:last-child tr:last-child > th:last-child,
-.table-bordered tfoot:last-child tr:last-child > td:last-child,
-.table-bordered tfoot:last-child tr:last-child > th:last-child {
- -webkit-border-bottom-right-radius: 4px;
- border-bottom-right-radius: 4px;
- -moz-border-radius-bottomright: 4px;
-}
-
-.table-bordered tfoot + tbody:last-child tr:last-child td:first-child {
- -webkit-border-bottom-left-radius: 0;
- border-bottom-left-radius: 0;
- -moz-border-radius-bottomleft: 0;
-}
-
-.table-bordered tfoot + tbody:last-child tr:last-child td:last-child {
- -webkit-border-bottom-right-radius: 0;
- border-bottom-right-radius: 0;
- -moz-border-radius-bottomright: 0;
-}
-
-.table-bordered caption + thead tr:first-child th:first-child,
-.table-bordered caption + tbody tr:first-child td:first-child,
-.table-bordered colgroup + thead tr:first-child th:first-child,
-.table-bordered colgroup + tbody tr:first-child td:first-child {
- -webkit-border-top-left-radius: 4px;
- border-top-left-radius: 4px;
- -moz-border-radius-topleft: 4px;
-}
-
-.table-bordered caption + thead tr:first-child th:last-child,
-.table-bordered caption + tbody tr:first-child td:last-child,
-.table-bordered colgroup + thead tr:first-child th:last-child,
-.table-bordered colgroup + tbody tr:first-child td:last-child {
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
- -moz-border-radius-topright: 4px;
-}
-
-.table-striped tbody > tr:nth-child(odd) > td,
-.table-striped tbody > tr:nth-child(odd) > th {
- background-color: #f9f9f9;
-}
-
-.table-hover tbody tr:hover > td,
-.table-hover tbody tr:hover > th {
- background-color: #f5f5f5;
-}
-
-table td[class*="span"],
-table th[class*="span"],
-.row-fluid table td[class*="span"],
-.row-fluid table th[class*="span"] {
- display: table-cell;
- float: none;
- margin-left: 0;
-}
-
-.table td.span1,
-.table th.span1 {
- float: none;
- width: 44px;
- margin-left: 0;
-}
-
-.table td.span2,
-.table th.span2 {
- float: none;
- width: 124px;
- margin-left: 0;
-}
-
-.table td.span3,
-.table th.span3 {
- float: none;
- width: 204px;
- margin-left: 0;
-}
-
-.table td.span4,
-.table th.span4 {
- float: none;
- width: 284px;
- margin-left: 0;
-}
-
-.table td.span5,
-.table th.span5 {
- float: none;
- width: 364px;
- margin-left: 0;
-}
-
-.table td.span6,
-.table th.span6 {
- float: none;
- width: 444px;
- margin-left: 0;
-}
-
-.table td.span7,
-.table th.span7 {
- float: none;
- width: 524px;
- margin-left: 0;
-}
-
-.table td.span8,
-.table th.span8 {
- float: none;
- width: 604px;
- margin-left: 0;
-}
-
-.table td.span9,
-.table th.span9 {
- float: none;
- width: 684px;
- margin-left: 0;
-}
-
-.table td.span10,
-.table th.span10 {
- float: none;
- width: 764px;
- margin-left: 0;
-}
-
-.table td.span11,
-.table th.span11 {
- float: none;
- width: 844px;
- margin-left: 0;
-}
-
-.table td.span12,
-.table th.span12 {
- float: none;
- width: 924px;
- margin-left: 0;
-}
-
-.table tbody tr.success > td {
- background-color: #dff0d8;
-}
-
-.table tbody tr.error > td {
- background-color: #f2dede;
-}
-
-.table tbody tr.warning > td {
- background-color: #fcf8e3;
-}
-
-.table tbody tr.info > td {
- background-color: #d9edf7;
-}
-
-.table-hover tbody tr.success:hover > td {
- background-color: #d0e9c6;
-}
-
-.table-hover tbody tr.error:hover > td {
- background-color: #ebcccc;
-}
-
-.table-hover tbody tr.warning:hover > td {
- background-color: #faf2cc;
-}
-
-.table-hover tbody tr.info:hover > td {
- background-color: #c4e3f3;
-}
-
-[class^="icon-"],
-[class*=" icon-"] {
- display: inline-block;
- width: 14px;
- height: 14px;
- margin-top: 1px;
- *margin-right: .3em;
- line-height: 14px;
- vertical-align: text-top;
- background-image: url("../img/glyphicons-halflings.png");
- background-position: 14px 14px;
- background-repeat: no-repeat;
-}
-
-/* White icons with optional class, or on hover/focus/active states of certain elements */
-
-.icon-white,
-.nav-pills > .active > a > [class^="icon-"],
-.nav-pills > .active > a > [class*=" icon-"],
-.nav-list > .active > a > [class^="icon-"],
-.nav-list > .active > a > [class*=" icon-"],
-.navbar-inverse .nav > .active > a > [class^="icon-"],
-.navbar-inverse .nav > .active > a > [class*=" icon-"],
-.dropdown-menu > li > a:hover > [class^="icon-"],
-.dropdown-menu > li > a:focus > [class^="icon-"],
-.dropdown-menu > li > a:hover > [class*=" icon-"],
-.dropdown-menu > li > a:focus > [class*=" icon-"],
-.dropdown-menu > .active > a > [class^="icon-"],
-.dropdown-menu > .active > a > [class*=" icon-"],
-.dropdown-submenu:hover > a > [class^="icon-"],
-.dropdown-submenu:focus > a > [class^="icon-"],
-.dropdown-submenu:hover > a > [class*=" icon-"],
-.dropdown-submenu:focus > a > [class*=" icon-"] {
- background-image: url("../img/glyphicons-halflings-white.png");
-}
-
-.icon-glass {
- background-position: 0 0;
-}
-
-.icon-music {
- background-position: -24px 0;
-}
-
-.icon-search {
- background-position: -48px 0;
-}
-
-.icon-envelope {
- background-position: -72px 0;
-}
-
-.icon-heart {
- background-position: -96px 0;
-}
-
-.icon-star {
- background-position: -120px 0;
-}
-
-.icon-star-empty {
- background-position: -144px 0;
-}
-
-.icon-user {
- background-position: -168px 0;
-}
-
-.icon-film {
- background-position: -192px 0;
-}
-
-.icon-th-large {
- background-position: -216px 0;
-}
-
-.icon-th {
- background-position: -240px 0;
-}
-
-.icon-th-list {
- background-position: -264px 0;
-}
-
-.icon-ok {
- background-position: -288px 0;
-}
-
-.icon-remove {
- background-position: -312px 0;
-}
-
-.icon-zoom-in {
- background-position: -336px 0;
-}
-
-.icon-zoom-out {
- background-position: -360px 0;
-}
-
-.icon-off {
- background-position: -384px 0;
-}
-
-.icon-signal {
- background-position: -408px 0;
-}
-
-.icon-cog {
- background-position: -432px 0;
-}
-
-.icon-trash {
- background-position: -456px 0;
-}
-
-.icon-home {
- background-position: 0 -24px;
-}
-
-.icon-file {
- background-position: -24px -24px;
-}
-
-.icon-time {
- background-position: -48px -24px;
-}
-
-.icon-road {
- background-position: -72px -24px;
-}
-
-.icon-download-alt {
- background-position: -96px -24px;
-}
-
-.icon-download {
- background-position: -120px -24px;
-}
-
-.icon-upload {
- background-position: -144px -24px;
-}
-
-.icon-inbox {
- background-position: -168px -24px;
-}
-
-.icon-play-circle {
- background-position: -192px -24px;
-}
-
-.icon-repeat {
- background-position: -216px -24px;
-}
-
-.icon-refresh {
- background-position: -240px -24px;
-}
-
-.icon-list-alt {
- background-position: -264px -24px;
-}
-
-.icon-lock {
- background-position: -287px -24px;
-}
-
-.icon-flag {
- background-position: -312px -24px;
-}
-
-.icon-headphones {
- background-position: -336px -24px;
-}
-
-.icon-volume-off {
- background-position: -360px -24px;
-}
-
-.icon-volume-down {
- background-position: -384px -24px;
-}
-
-.icon-volume-up {
- background-position: -408px -24px;
-}
-
-.icon-qrcode {
- background-position: -432px -24px;
-}
-
-.icon-barcode {
- background-position: -456px -24px;
-}
-
-.icon-tag {
- background-position: 0 -48px;
-}
-
-.icon-tags {
- background-position: -25px -48px;
-}
-
-.icon-book {
- background-position: -48px -48px;
-}
-
-.icon-bookmark {
- background-position: -72px -48px;
-}
-
-.icon-print {
- background-position: -96px -48px;
-}
-
-.icon-camera {
- background-position: -120px -48px;
-}
-
-.icon-font {
- background-position: -144px -48px;
-}
-
-.icon-bold {
- background-position: -167px -48px;
-}
-
-.icon-italic {
- background-position: -192px -48px;
-}
-
-.icon-text-height {
- background-position: -216px -48px;
-}
-
-.icon-text-width {
- background-position: -240px -48px;
-}
-
-.icon-align-left {
- background-position: -264px -48px;
-}
-
-.icon-align-center {
- background-position: -288px -48px;
-}
-
-.icon-align-right {
- background-position: -312px -48px;
-}
-
-.icon-align-justify {
- background-position: -336px -48px;
-}
-
-.icon-list {
- background-position: -360px -48px;
-}
-
-.icon-indent-left {
- background-position: -384px -48px;
-}
-
-.icon-indent-right {
- background-position: -408px -48px;
-}
-
-.icon-facetime-video {
- background-position: -432px -48px;
-}
-
-.icon-picture {
- background-position: -456px -48px;
-}
-
-.icon-pencil {
- background-position: 0 -72px;
-}
-
-.icon-map-marker {
- background-position: -24px -72px;
-}
-
-.icon-adjust {
- background-position: -48px -72px;
-}
-
-.icon-tint {
- background-position: -72px -72px;
-}
-
-.icon-edit {
- background-position: -96px -72px;
-}
-
-.icon-share {
- background-position: -120px -72px;
-}
-
-.icon-check {
- background-position: -144px -72px;
-}
-
-.icon-move {
- background-position: -168px -72px;
-}
-
-.icon-step-backward {
- background-position: -192px -72px;
-}
-
-.icon-fast-backward {
- background-position: -216px -72px;
-}
-
-.icon-backward {
- background-position: -240px -72px;
-}
-
-.icon-play {
- background-position: -264px -72px;
-}
-
-.icon-pause {
- background-position: -288px -72px;
-}
-
-.icon-stop {
- background-position: -312px -72px;
-}
-
-.icon-forward {
- background-position: -336px -72px;
-}
-
-.icon-fast-forward {
- background-position: -360px -72px;
-}
-
-.icon-step-forward {
- background-position: -384px -72px;
-}
-
-.icon-eject {
- background-position: -408px -72px;
-}
-
-.icon-chevron-left {
- background-position: -432px -72px;
-}
-
-.icon-chevron-right {
- background-position: -456px -72px;
-}
-
-.icon-plus-sign {
- background-position: 0 -96px;
-}
-
-.icon-minus-sign {
- background-position: -24px -96px;
-}
-
-.icon-remove-sign {
- background-position: -48px -96px;
-}
-
-.icon-ok-sign {
- background-position: -72px -96px;
-}
-
-.icon-question-sign {
- background-position: -96px -96px;
-}
-
-.icon-info-sign {
- background-position: -120px -96px;
-}
-
-.icon-screenshot {
- background-position: -144px -96px;
-}
-
-.icon-remove-circle {
- background-position: -168px -96px;
-}
-
-.icon-ok-circle {
- background-position: -192px -96px;
-}
-
-.icon-ban-circle {
- background-position: -216px -96px;
-}
-
-.icon-arrow-left {
- background-position: -240px -96px;
-}
-
-.icon-arrow-right {
- background-position: -264px -96px;
-}
-
-.icon-arrow-up {
- background-position: -289px -96px;
-}
-
-.icon-arrow-down {
- background-position: -312px -96px;
-}
-
-.icon-share-alt {
- background-position: -336px -96px;
-}
-
-.icon-resize-full {
- background-position: -360px -96px;
-}
-
-.icon-resize-small {
- background-position: -384px -96px;
-}
-
-.icon-plus {
- background-position: -408px -96px;
-}
-
-.icon-minus {
- background-position: -433px -96px;
-}
-
-.icon-asterisk {
- background-position: -456px -96px;
-}
-
-.icon-exclamation-sign {
- background-position: 0 -120px;
-}
-
-.icon-gift {
- background-position: -24px -120px;
-}
-
-.icon-leaf {
- background-position: -48px -120px;
-}
-
-.icon-fire {
- background-position: -72px -120px;
-}
-
-.icon-eye-open {
- background-position: -96px -120px;
-}
-
-.icon-eye-close {
- background-position: -120px -120px;
-}
-
-.icon-warning-sign {
- background-position: -144px -120px;
-}
-
-.icon-plane {
- background-position: -168px -120px;
-}
-
-.icon-calendar {
- background-position: -192px -120px;
-}
-
-.icon-random {
- width: 16px;
- background-position: -216px -120px;
-}
-
-.icon-comment {
- background-position: -240px -120px;
-}
-
-.icon-magnet {
- background-position: -264px -120px;
-}
-
-.icon-chevron-up {
- background-position: -288px -120px;
-}
-
-.icon-chevron-down {
- background-position: -313px -119px;
-}
-
-.icon-retweet {
- background-position: -336px -120px;
-}
-
-.icon-shopping-cart {
- background-position: -360px -120px;
-}
-
-.icon-folder-close {
- width: 16px;
- background-position: -384px -120px;
-}
-
-.icon-folder-open {
- width: 16px;
- background-position: -408px -120px;
-}
-
-.icon-resize-vertical {
- background-position: -432px -119px;
-}
-
-.icon-resize-horizontal {
- background-position: -456px -118px;
-}
-
-.icon-hdd {
- background-position: 0 -144px;
-}
-
-.icon-bullhorn {
- background-position: -24px -144px;
-}
-
-.icon-bell {
- background-position: -48px -144px;
-}
-
-.icon-certificate {
- background-position: -72px -144px;
-}
-
-.icon-thumbs-up {
- background-position: -96px -144px;
-}
-
-.icon-thumbs-down {
- background-position: -120px -144px;
-}
-
-.icon-hand-right {
- background-position: -144px -144px;
-}
-
-.icon-hand-left {
- background-position: -168px -144px;
-}
-
-.icon-hand-up {
- background-position: -192px -144px;
-}
-
-.icon-hand-down {
- background-position: -216px -144px;
-}
-
-.icon-circle-arrow-right {
- background-position: -240px -144px;
-}
-
-.icon-circle-arrow-left {
- background-position: -264px -144px;
-}
-
-.icon-circle-arrow-up {
- background-position: -288px -144px;
-}
-
-.icon-circle-arrow-down {
- background-position: -312px -144px;
-}
-
-.icon-globe {
- background-position: -336px -144px;
-}
-
-.icon-wrench {
- background-position: -360px -144px;
-}
-
-.icon-tasks {
- background-position: -384px -144px;
-}
-
-.icon-filter {
- background-position: -408px -144px;
-}
-
-.icon-briefcase {
- background-position: -432px -144px;
-}
-
-.icon-fullscreen {
- background-position: -456px -144px;
-}
-
-.dropup,
-.dropdown {
- position: relative;
-}
-
-.dropdown-toggle {
- *margin-bottom: -3px;
-}
-
-.dropdown-toggle:active,
-.open .dropdown-toggle {
- outline: 0;
-}
-
-.caret {
- display: inline-block;
- width: 0;
- height: 0;
- vertical-align: top;
- border-top: 4px solid #000000;
- border-right: 4px solid transparent;
- border-left: 4px solid transparent;
- content: "";
-}
-
-.dropdown .caret {
- margin-top: 8px;
- margin-left: 2px;
-}
-
-.dropdown-menu {
- position: absolute;
- top: 100%;
- left: 0;
- z-index: 1000;
- display: none;
- float: left;
- min-width: 160px;
- padding: 5px 0;
- margin: 2px 0 0;
- list-style: none;
- background-color: #ffffff;
- border: 1px solid #ccc;
- border: 1px solid rgba(0, 0, 0, 0.2);
- *border-right-width: 2px;
- *border-bottom-width: 2px;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
- -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding;
- background-clip: padding-box;
-}
-
-.dropdown-menu.pull-right {
- right: 0;
- left: auto;
-}
-
-.dropdown-menu .divider {
- *width: 100%;
- height: 1px;
- margin: 9px 1px;
- *margin: -5px 0 5px;
- overflow: hidden;
- background-color: #e5e5e5;
- border-bottom: 1px solid #ffffff;
-}
-
-.dropdown-menu > li > a {
- display: block;
- padding: 3px 20px;
- clear: both;
- font-weight: normal;
- line-height: 20px;
- color: #333333;
- white-space: nowrap;
-}
-
-.dropdown-menu > li > a:hover,
-.dropdown-menu > li > a:focus,
-.dropdown-submenu:hover > a,
-.dropdown-submenu:focus > a {
- color: #ffffff;
- text-decoration: none;
- background-color: #0081c2;
- background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
- background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
- background-image: -o-linear-gradient(top, #0088cc, #0077b3);
- background-image: linear-gradient(to bottom, #0088cc, #0077b3);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
-}
-
-.dropdown-menu > .active > a,
-.dropdown-menu > .active > a:hover,
-.dropdown-menu > .active > a:focus {
- color: #ffffff;
- text-decoration: none;
- background-color: #0081c2;
- background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
- background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
- background-image: -o-linear-gradient(top, #0088cc, #0077b3);
- background-image: linear-gradient(to bottom, #0088cc, #0077b3);
- background-repeat: repeat-x;
- outline: 0;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
-}
-
-.dropdown-menu > .disabled > a,
-.dropdown-menu > .disabled > a:hover,
-.dropdown-menu > .disabled > a:focus {
- color: #999999;
-}
-
-.dropdown-menu > .disabled > a:hover,
-.dropdown-menu > .disabled > a:focus {
- text-decoration: none;
- cursor: default;
- background-color: transparent;
- background-image: none;
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-}
-
-.open {
- *z-index: 1000;
-}
-
-.open > .dropdown-menu {
- display: block;
-}
-
-.dropdown-backdrop {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 990;
-}
-
-.pull-right > .dropdown-menu {
- right: 0;
- left: auto;
-}
-
-.dropup .caret,
-.navbar-fixed-bottom .dropdown .caret {
- border-top: 0;
- border-bottom: 4px solid #000000;
- content: "";
-}
-
-.dropup .dropdown-menu,
-.navbar-fixed-bottom .dropdown .dropdown-menu {
- top: auto;
- bottom: 100%;
- margin-bottom: 1px;
-}
-
-.dropdown-submenu {
- position: relative;
-}
-
-.dropdown-submenu > .dropdown-menu {
- top: 0;
- left: 100%;
- margin-top: -6px;
- margin-left: -1px;
- -webkit-border-radius: 0 6px 6px 6px;
- -moz-border-radius: 0 6px 6px 6px;
- border-radius: 0 6px 6px 6px;
-}
-
-.dropdown-submenu:hover > .dropdown-menu {
- display: block;
-}
-
-.dropup .dropdown-submenu > .dropdown-menu {
- top: auto;
- bottom: 0;
- margin-top: 0;
- margin-bottom: -2px;
- -webkit-border-radius: 5px 5px 5px 0;
- -moz-border-radius: 5px 5px 5px 0;
- border-radius: 5px 5px 5px 0;
-}
-
-.dropdown-submenu > a:after {
- display: block;
- float: right;
- width: 0;
- height: 0;
- margin-top: 5px;
- margin-right: -10px;
- border-color: transparent;
- border-left-color: #cccccc;
- border-style: solid;
- border-width: 5px 0 5px 5px;
- content: " ";
-}
-
-.dropdown-submenu:hover > a:after {
- border-left-color: #ffffff;
-}
-
-.dropdown-submenu.pull-left {
- float: none;
-}
-
-.dropdown-submenu.pull-left > .dropdown-menu {
- left: -100%;
- margin-left: 10px;
- -webkit-border-radius: 6px 0 6px 6px;
- -moz-border-radius: 6px 0 6px 6px;
- border-radius: 6px 0 6px 6px;
-}
-
-.dropdown .dropdown-menu .nav-header {
- padding-right: 20px;
- padding-left: 20px;
-}
-
-.typeahead {
- z-index: 1051;
- margin-top: 2px;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-
-.well {
- min-height: 20px;
- padding: 19px;
- margin-bottom: 20px;
- background-color: #f5f5f5;
- border: 1px solid #e3e3e3;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
-}
-
-.well blockquote {
- border-color: #ddd;
- border-color: rgba(0, 0, 0, 0.15);
-}
-
-.well-large {
- padding: 24px;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
-}
-
-.well-small {
- padding: 9px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-
-.fade {
- opacity: 0;
- -webkit-transition: opacity 0.15s linear;
- -moz-transition: opacity 0.15s linear;
- -o-transition: opacity 0.15s linear;
- transition: opacity 0.15s linear;
-}
-
-.fade.in {
- opacity: 1;
-}
-
-.collapse {
- position: relative;
- height: 0;
- overflow: hidden;
- -webkit-transition: height 0.35s ease;
- -moz-transition: height 0.35s ease;
- -o-transition: height 0.35s ease;
- transition: height 0.35s ease;
-}
-
-.collapse.in {
- height: auto;
-}
-
-.close {
- float: right;
- font-size: 20px;
- font-weight: bold;
- line-height: 20px;
- color: #000000;
- text-shadow: 0 1px 0 #ffffff;
- opacity: 0.2;
- filter: alpha(opacity=20);
-}
-
-.close:hover,
-.close:focus {
- color: #000000;
- text-decoration: none;
- cursor: pointer;
- opacity: 0.4;
- filter: alpha(opacity=40);
-}
-
-button.close {
- padding: 0;
- cursor: pointer;
- background: transparent;
- border: 0;
- -webkit-appearance: none;
-}
-
-.btn {
- display: inline-block;
- *display: inline;
- padding: 4px 12px;
- margin-bottom: 0;
- *margin-left: .3em;
- font-size: 14px;
- line-height: 20px;
- color: #333333;
- text-align: center;
- text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
- vertical-align: middle;
- cursor: pointer;
- background-color: #f5f5f5;
- *background-color: #e6e6e6;
- background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
- background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
- background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
- background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
- background-repeat: repeat-x;
- border: 1px solid #cccccc;
- *border: 0;
- border-color: #e6e6e6 #e6e6e6 #bfbfbf;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- border-bottom-color: #b3b3b3;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
- *zoom: 1;
- -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-
-.btn:hover,
-.btn:focus,
-.btn:active,
-.btn.active,
-.btn.disabled,
-.btn[disabled] {
- color: #333333;
- background-color: #e6e6e6;
- *background-color: #d9d9d9;
-}
-
-.btn:active,
-.btn.active {
- background-color: #cccccc \9;
-}
-
-.btn:first-child {
- *margin-left: 0;
-}
-
-.btn:hover,
-.btn:focus {
- color: #333333;
- text-decoration: none;
- background-position: 0 -15px;
- -webkit-transition: background-position 0.1s linear;
- -moz-transition: background-position 0.1s linear;
- -o-transition: background-position 0.1s linear;
- transition: background-position 0.1s linear;
-}
-
-.btn:focus {
- outline: thin dotted #333;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
-}
-
-.btn.active,
-.btn:active {
- background-image: none;
- outline: 0;
- -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
- box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-
-.btn.disabled,
-.btn[disabled] {
- cursor: default;
- background-image: none;
- opacity: 0.65;
- filter: alpha(opacity=65);
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
-}
-
-.btn-large {
- padding: 11px 19px;
- font-size: 17.5px;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
-}
-
-.btn-large [class^="icon-"],
-.btn-large [class*=" icon-"] {
- margin-top: 4px;
-}
-
-.btn-small {
- padding: 2px 10px;
- font-size: 11.9px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-
-.btn-small [class^="icon-"],
-.btn-small [class*=" icon-"] {
- margin-top: 0;
-}
-
-.btn-mini [class^="icon-"],
-.btn-mini [class*=" icon-"] {
- margin-top: -1px;
-}
-
-.btn-mini {
- padding: 0 6px;
- font-size: 10.5px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-
-.btn-block {
- display: block;
- width: 100%;
- padding-right: 0;
- padding-left: 0;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-
-.btn-block + .btn-block {
- margin-top: 5px;
-}
-
-input[type="submit"].btn-block,
-input[type="reset"].btn-block,
-input[type="button"].btn-block {
- width: 100%;
-}
-
-.btn-primary.active,
-.btn-warning.active,
-.btn-danger.active,
-.btn-success.active,
-.btn-info.active,
-.btn-inverse.active {
- color: rgba(255, 255, 255, 0.75);
-}
-
-.btn-primary {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #006dcc;
- *background-color: #0044cc;
- background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
- background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
- background-image: -o-linear-gradient(top, #0088cc, #0044cc);
- background-image: linear-gradient(to bottom, #0088cc, #0044cc);
- background-repeat: repeat-x;
- border-color: #0044cc #0044cc #002a80;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-}
-
-.btn-primary:hover,
-.btn-primary:focus,
-.btn-primary:active,
-.btn-primary.active,
-.btn-primary.disabled,
-.btn-primary[disabled] {
- color: #ffffff;
- background-color: #0044cc;
- *background-color: #003bb3;
-}
-
-.btn-primary:active,
-.btn-primary.active {
- background-color: #003399 \9;
-}
-
-.btn-warning {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #faa732;
- *background-color: #f89406;
- background-image: -moz-linear-gradient(top, #fbb450, #f89406);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
- background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
- background-image: -o-linear-gradient(top, #fbb450, #f89406);
- background-image: linear-gradient(to bottom, #fbb450, #f89406);
- background-repeat: repeat-x;
- border-color: #f89406 #f89406 #ad6704;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-}
-
-.btn-warning:hover,
-.btn-warning:focus,
-.btn-warning:active,
-.btn-warning.active,
-.btn-warning.disabled,
-.btn-warning[disabled] {
- color: #ffffff;
- background-color: #f89406;
- *background-color: #df8505;
-}
-
-.btn-warning:active,
-.btn-warning.active {
- background-color: #c67605 \9;
-}
-
-.btn-danger {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #da4f49;
- *background-color: #bd362f;
- background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
- background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
- background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
- background-image: linear-gradient(to bottom, #ee5f5b, #bd362f);
- background-repeat: repeat-x;
- border-color: #bd362f #bd362f #802420;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-}
-
-.btn-danger:hover,
-.btn-danger:focus,
-.btn-danger:active,
-.btn-danger.active,
-.btn-danger.disabled,
-.btn-danger[disabled] {
- color: #ffffff;
- background-color: #bd362f;
- *background-color: #a9302a;
-}
-
-.btn-danger:active,
-.btn-danger.active {
- background-color: #942a25 \9;
-}
-
-.btn-success {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #5bb75b;
- *background-color: #51a351;
- background-image: -moz-linear-gradient(top, #62c462, #51a351);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
- background-image: -webkit-linear-gradient(top, #62c462, #51a351);
- background-image: -o-linear-gradient(top, #62c462, #51a351);
- background-image: linear-gradient(to bottom, #62c462, #51a351);
- background-repeat: repeat-x;
- border-color: #51a351 #51a351 #387038;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-}
-
-.btn-success:hover,
-.btn-success:focus,
-.btn-success:active,
-.btn-success.active,
-.btn-success.disabled,
-.btn-success[disabled] {
- color: #ffffff;
- background-color: #51a351;
- *background-color: #499249;
-}
-
-.btn-success:active,
-.btn-success.active {
- background-color: #408140 \9;
-}
-
-.btn-info {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #49afcd;
- *background-color: #2f96b4;
- background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));
- background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
- background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
- background-image: linear-gradient(to bottom, #5bc0de, #2f96b4);
- background-repeat: repeat-x;
- border-color: #2f96b4 #2f96b4 #1f6377;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-}
-
-.btn-info:hover,
-.btn-info:focus,
-.btn-info:active,
-.btn-info.active,
-.btn-info.disabled,
-.btn-info[disabled] {
- color: #ffffff;
- background-color: #2f96b4;
- *background-color: #2a85a0;
-}
-
-.btn-info:active,
-.btn-info.active {
- background-color: #24748c \9;
-}
-
-.btn-inverse {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #363636;
- *background-color: #222222;
- background-image: -moz-linear-gradient(top, #444444, #222222);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));
- background-image: -webkit-linear-gradient(top, #444444, #222222);
- background-image: -o-linear-gradient(top, #444444, #222222);
- background-image: linear-gradient(to bottom, #444444, #222222);
- background-repeat: repeat-x;
- border-color: #222222 #222222 #000000;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-}
-
-.btn-inverse:hover,
-.btn-inverse:focus,
-.btn-inverse:active,
-.btn-inverse.active,
-.btn-inverse.disabled,
-.btn-inverse[disabled] {
- color: #ffffff;
- background-color: #222222;
- *background-color: #151515;
-}
-
-.btn-inverse:active,
-.btn-inverse.active {
- background-color: #080808 \9;
-}
-
-button.btn,
-input[type="submit"].btn {
- *padding-top: 3px;
- *padding-bottom: 3px;
-}
-
-button.btn::-moz-focus-inner,
-input[type="submit"].btn::-moz-focus-inner {
- padding: 0;
- border: 0;
-}
-
-button.btn.btn-large,
-input[type="submit"].btn.btn-large {
- *padding-top: 7px;
- *padding-bottom: 7px;
-}
-
-button.btn.btn-small,
-input[type="submit"].btn.btn-small {
- *padding-top: 3px;
- *padding-bottom: 3px;
-}
-
-button.btn.btn-mini,
-input[type="submit"].btn.btn-mini {
- *padding-top: 1px;
- *padding-bottom: 1px;
-}
-
-.btn-link,
-.btn-link:active,
-.btn-link[disabled] {
- background-color: transparent;
- background-image: none;
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
-}
-
-.btn-link {
- color: #0088cc;
- cursor: pointer;
- border-color: transparent;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
-}
-
-.btn-link:hover,
-.btn-link:focus {
- color: #005580;
- text-decoration: underline;
- background-color: transparent;
-}
-
-.btn-link[disabled]:hover,
-.btn-link[disabled]:focus {
- color: #333333;
- text-decoration: none;
-}
-
-.btn-group {
- position: relative;
- display: inline-block;
- *display: inline;
- *margin-left: .3em;
- font-size: 0;
- white-space: nowrap;
- vertical-align: middle;
- *zoom: 1;
-}
-
-.btn-group:first-child {
- *margin-left: 0;
-}
-
-.btn-group + .btn-group {
- margin-left: 5px;
-}
-
-.btn-toolbar {
- margin-top: 10px;
- margin-bottom: 10px;
- font-size: 0;
-}
-
-.btn-toolbar > .btn + .btn,
-.btn-toolbar > .btn-group + .btn,
-.btn-toolbar > .btn + .btn-group {
- margin-left: 5px;
-}
-
-.btn-group > .btn {
- position: relative;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
-}
-
-.btn-group > .btn + .btn {
- margin-left: -1px;
-}
-
-.btn-group > .btn,
-.btn-group > .dropdown-menu,
-.btn-group > .popover {
- font-size: 14px;
-}
-
-.btn-group > .btn-mini {
- font-size: 10.5px;
-}
-
-.btn-group > .btn-small {
- font-size: 11.9px;
-}
-
-.btn-group > .btn-large {
- font-size: 17.5px;
-}
-
-.btn-group > .btn:first-child {
- margin-left: 0;
- -webkit-border-bottom-left-radius: 4px;
- border-bottom-left-radius: 4px;
- -webkit-border-top-left-radius: 4px;
- border-top-left-radius: 4px;
- -moz-border-radius-bottomleft: 4px;
- -moz-border-radius-topleft: 4px;
-}
-
-.btn-group > .btn:last-child,
-.btn-group > .dropdown-toggle {
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
- -webkit-border-bottom-right-radius: 4px;
- border-bottom-right-radius: 4px;
- -moz-border-radius-topright: 4px;
- -moz-border-radius-bottomright: 4px;
-}
-
-.btn-group > .btn.large:first-child {
- margin-left: 0;
- -webkit-border-bottom-left-radius: 6px;
- border-bottom-left-radius: 6px;
- -webkit-border-top-left-radius: 6px;
- border-top-left-radius: 6px;
- -moz-border-radius-bottomleft: 6px;
- -moz-border-radius-topleft: 6px;
-}
-
-.btn-group > .btn.large:last-child,
-.btn-group > .large.dropdown-toggle {
- -webkit-border-top-right-radius: 6px;
- border-top-right-radius: 6px;
- -webkit-border-bottom-right-radius: 6px;
- border-bottom-right-radius: 6px;
- -moz-border-radius-topright: 6px;
- -moz-border-radius-bottomright: 6px;
-}
-
-.btn-group > .btn:hover,
-.btn-group > .btn:focus,
-.btn-group > .btn:active,
-.btn-group > .btn.active {
- z-index: 2;
-}
-
-.btn-group .dropdown-toggle:active,
-.btn-group.open .dropdown-toggle {
- outline: 0;
-}
-
-.btn-group > .btn + .dropdown-toggle {
- *padding-top: 5px;
- padding-right: 8px;
- *padding-bottom: 5px;
- padding-left: 8px;
- -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
- box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-
-.btn-group > .btn-mini + .dropdown-toggle {
- *padding-top: 2px;
- padding-right: 5px;
- *padding-bottom: 2px;
- padding-left: 5px;
-}
-
-.btn-group > .btn-small + .dropdown-toggle {
- *padding-top: 5px;
- *padding-bottom: 4px;
-}
-
-.btn-group > .btn-large + .dropdown-toggle {
- *padding-top: 7px;
- padding-right: 12px;
- *padding-bottom: 7px;
- padding-left: 12px;
-}
-
-.btn-group.open .dropdown-toggle {
- background-image: none;
- -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
- box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-
-.btn-group.open .btn.dropdown-toggle {
- background-color: #e6e6e6;
-}
-
-.btn-group.open .btn-primary.dropdown-toggle {
- background-color: #0044cc;
-}
-
-.btn-group.open .btn-warning.dropdown-toggle {
- background-color: #f89406;
-}
-
-.btn-group.open .btn-danger.dropdown-toggle {
- background-color: #bd362f;
-}
-
-.btn-group.open .btn-success.dropdown-toggle {
- background-color: #51a351;
-}
-
-.btn-group.open .btn-info.dropdown-toggle {
- background-color: #2f96b4;
-}
-
-.btn-group.open .btn-inverse.dropdown-toggle {
- background-color: #222222;
-}
-
-.btn .caret {
- margin-top: 8px;
- margin-left: 0;
-}
-
-.btn-large .caret {
- margin-top: 6px;
-}
-
-.btn-large .caret {
- border-top-width: 5px;
- border-right-width: 5px;
- border-left-width: 5px;
-}
-
-.btn-mini .caret,
-.btn-small .caret {
- margin-top: 8px;
-}
-
-.dropup .btn-large .caret {
- border-bottom-width: 5px;
-}
-
-.btn-primary .caret,
-.btn-warning .caret,
-.btn-danger .caret,
-.btn-info .caret,
-.btn-success .caret,
-.btn-inverse .caret {
- border-top-color: #ffffff;
- border-bottom-color: #ffffff;
-}
-
-.btn-group-vertical {
- display: inline-block;
- *display: inline;
- /* IE7 inline-block hack */
-
- *zoom: 1;
-}
-
-.btn-group-vertical > .btn {
- display: block;
- float: none;
- max-width: 100%;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
-}
-
-.btn-group-vertical > .btn + .btn {
- margin-top: -1px;
- margin-left: 0;
-}
-
-.btn-group-vertical > .btn:first-child {
- -webkit-border-radius: 4px 4px 0 0;
- -moz-border-radius: 4px 4px 0 0;
- border-radius: 4px 4px 0 0;
-}
-
-.btn-group-vertical > .btn:last-child {
- -webkit-border-radius: 0 0 4px 4px;
- -moz-border-radius: 0 0 4px 4px;
- border-radius: 0 0 4px 4px;
-}
-
-.btn-group-vertical > .btn-large:first-child {
- -webkit-border-radius: 6px 6px 0 0;
- -moz-border-radius: 6px 6px 0 0;
- border-radius: 6px 6px 0 0;
-}
-
-.btn-group-vertical > .btn-large:last-child {
- -webkit-border-radius: 0 0 6px 6px;
- -moz-border-radius: 0 0 6px 6px;
- border-radius: 0 0 6px 6px;
-}
-
-.alert {
- padding: 8px 35px 8px 14px;
- margin-bottom: 20px;
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
- background-color: #fcf8e3;
- border: 1px solid #fbeed5;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-
-.alert,
-.alert h4 {
- color: #c09853;
-}
-
-.alert h4 {
- margin: 0;
-}
-
-.alert .close {
- position: relative;
- top: -2px;
- right: -21px;
- line-height: 20px;
-}
-
-.alert-success {
- color: #468847;
- background-color: #dff0d8;
- border-color: #d6e9c6;
-}
-
-.alert-success h4 {
- color: #468847;
-}
-
-.alert-danger,
-.alert-error {
- color: #b94a48;
- background-color: #f2dede;
- border-color: #eed3d7;
-}
-
-.alert-danger h4,
-.alert-error h4 {
- color: #b94a48;
-}
-
-.alert-info {
- color: #3a87ad;
- background-color: #d9edf7;
- border-color: #bce8f1;
-}
-
-.alert-info h4 {
- color: #3a87ad;
-}
-
-.alert-block {
- padding-top: 14px;
- padding-bottom: 14px;
-}
-
-.alert-block > p,
-.alert-block > ul {
- margin-bottom: 0;
-}
-
-.alert-block p + p {
- margin-top: 5px;
-}
-
-.nav {
- margin-bottom: 20px;
- margin-left: 0;
- list-style: none;
-}
-
-.nav > li > a {
- display: block;
-}
-
-.nav > li > a:hover,
-.nav > li > a:focus {
- text-decoration: none;
- background-color: #eeeeee;
-}
-
-.nav > li > a > img {
- max-width: none;
-}
-
-.nav > .pull-right {
- float: right;
-}
-
-.nav-header {
- display: block;
- padding: 3px 15px;
- font-size: 11px;
- font-weight: bold;
- line-height: 20px;
- color: #999999;
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
- text-transform: uppercase;
-}
-
-.nav li + .nav-header {
- margin-top: 9px;
-}
-
-.nav-list {
- padding-right: 15px;
- padding-left: 15px;
- margin-bottom: 0;
-}
-
-.nav-list > li > a,
-.nav-list .nav-header {
- margin-right: -15px;
- margin-left: -15px;
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
-}
-
-.nav-list > li > a {
- padding: 3px 15px;
-}
-
-.nav-list > .active > a,
-.nav-list > .active > a:hover,
-.nav-list > .active > a:focus {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
- background-color: #0088cc;
-}
-
-.nav-list [class^="icon-"],
-.nav-list [class*=" icon-"] {
- margin-right: 2px;
-}
-
-.nav-list .divider {
- *width: 100%;
- height: 1px;
- margin: 9px 1px;
- *margin: -5px 0 5px;
- overflow: hidden;
- background-color: #e5e5e5;
- border-bottom: 1px solid #ffffff;
-}
-
-.nav-tabs,
-.nav-pills {
- *zoom: 1;
-}
-
-.nav-tabs:before,
-.nav-pills:before,
-.nav-tabs:after,
-.nav-pills:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.nav-tabs:after,
-.nav-pills:after {
- clear: both;
-}
-
-.nav-tabs > li,
-.nav-pills > li {
- float: left;
-}
-
-.nav-tabs > li > a,
-.nav-pills > li > a {
- padding-right: 12px;
- padding-left: 12px;
- margin-right: 2px;
- line-height: 14px;
-}
-
-.nav-tabs {
- border-bottom: 1px solid #ddd;
-}
-
-.nav-tabs > li {
- margin-bottom: -1px;
-}
-
-.nav-tabs > li > a {
- padding-top: 8px;
- padding-bottom: 8px;
- line-height: 20px;
- border: 1px solid transparent;
- -webkit-border-radius: 4px 4px 0 0;
- -moz-border-radius: 4px 4px 0 0;
- border-radius: 4px 4px 0 0;
-}
-
-.nav-tabs > li > a:hover,
-.nav-tabs > li > a:focus {
- border-color: #eeeeee #eeeeee #dddddd;
-}
-
-.nav-tabs > .active > a,
-.nav-tabs > .active > a:hover,
-.nav-tabs > .active > a:focus {
- color: #555555;
- cursor: default;
- background-color: #ffffff;
- border: 1px solid #ddd;
- border-bottom-color: transparent;
-}
-
-.nav-pills > li > a {
- padding-top: 8px;
- padding-bottom: 8px;
- margin-top: 2px;
- margin-bottom: 2px;
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- border-radius: 5px;
-}
-
-.nav-pills > .active > a,
-.nav-pills > .active > a:hover,
-.nav-pills > .active > a:focus {
- color: #ffffff;
- background-color: #0088cc;
-}
-
-.nav-stacked > li {
- float: none;
-}
-
-.nav-stacked > li > a {
- margin-right: 0;
-}
-
-.nav-tabs.nav-stacked {
- border-bottom: 0;
-}
-
-.nav-tabs.nav-stacked > li > a {
- border: 1px solid #ddd;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
-}
-
-.nav-tabs.nav-stacked > li:first-child > a {
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
- -webkit-border-top-left-radius: 4px;
- border-top-left-radius: 4px;
- -moz-border-radius-topright: 4px;
- -moz-border-radius-topleft: 4px;
-}
-
-.nav-tabs.nav-stacked > li:last-child > a {
- -webkit-border-bottom-right-radius: 4px;
- border-bottom-right-radius: 4px;
- -webkit-border-bottom-left-radius: 4px;
- border-bottom-left-radius: 4px;
- -moz-border-radius-bottomright: 4px;
- -moz-border-radius-bottomleft: 4px;
-}
-
-.nav-tabs.nav-stacked > li > a:hover,
-.nav-tabs.nav-stacked > li > a:focus {
- z-index: 2;
- border-color: #ddd;
-}
-
-.nav-pills.nav-stacked > li > a {
- margin-bottom: 3px;
-}
-
-.nav-pills.nav-stacked > li:last-child > a {
- margin-bottom: 1px;
-}
-
-.nav-tabs .dropdown-menu {
- -webkit-border-radius: 0 0 6px 6px;
- -moz-border-radius: 0 0 6px 6px;
- border-radius: 0 0 6px 6px;
-}
-
-.nav-pills .dropdown-menu {
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
-}
-
-.nav .dropdown-toggle .caret {
- margin-top: 6px;
- border-top-color: #0088cc;
- border-bottom-color: #0088cc;
-}
-
-.nav .dropdown-toggle:hover .caret,
-.nav .dropdown-toggle:focus .caret {
- border-top-color: #005580;
- border-bottom-color: #005580;
-}
-
-/* move down carets for tabs */
-
-.nav-tabs .dropdown-toggle .caret {
- margin-top: 8px;
-}
-
-.nav .active .dropdown-toggle .caret {
- border-top-color: #fff;
- border-bottom-color: #fff;
-}
-
-.nav-tabs .active .dropdown-toggle .caret {
- border-top-color: #555555;
- border-bottom-color: #555555;
-}
-
-.nav > .dropdown.active > a:hover,
-.nav > .dropdown.active > a:focus {
- cursor: pointer;
-}
-
-.nav-tabs .open .dropdown-toggle,
-.nav-pills .open .dropdown-toggle,
-.nav > li.dropdown.open.active > a:hover,
-.nav > li.dropdown.open.active > a:focus {
- color: #ffffff;
- background-color: #999999;
- border-color: #999999;
-}
-
-.nav li.dropdown.open .caret,
-.nav li.dropdown.open.active .caret,
-.nav li.dropdown.open a:hover .caret,
-.nav li.dropdown.open a:focus .caret {
- border-top-color: #ffffff;
- border-bottom-color: #ffffff;
- opacity: 1;
- filter: alpha(opacity=100);
-}
-
-.tabs-stacked .open > a:hover,
-.tabs-stacked .open > a:focus {
- border-color: #999999;
-}
-
-.tabbable {
- *zoom: 1;
-}
-
-.tabbable:before,
-.tabbable:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.tabbable:after {
- clear: both;
-}
-
-.tab-content {
- overflow: auto;
-}
-
-.tabs-below > .nav-tabs,
-.tabs-right > .nav-tabs,
-.tabs-left > .nav-tabs {
- border-bottom: 0;
-}
-
-.tab-content > .tab-pane,
-.pill-content > .pill-pane {
- display: none;
-}
-
-.tab-content > .active,
-.pill-content > .active {
- display: block;
-}
-
-.tabs-below > .nav-tabs {
- border-top: 1px solid #ddd;
-}
-
-.tabs-below > .nav-tabs > li {
- margin-top: -1px;
- margin-bottom: 0;
-}
-
-.tabs-below > .nav-tabs > li > a {
- -webkit-border-radius: 0 0 4px 4px;
- -moz-border-radius: 0 0 4px 4px;
- border-radius: 0 0 4px 4px;
-}
-
-.tabs-below > .nav-tabs > li > a:hover,
-.tabs-below > .nav-tabs > li > a:focus {
- border-top-color: #ddd;
- border-bottom-color: transparent;
-}
-
-.tabs-below > .nav-tabs > .active > a,
-.tabs-below > .nav-tabs > .active > a:hover,
-.tabs-below > .nav-tabs > .active > a:focus {
- border-color: transparent #ddd #ddd #ddd;
-}
-
-.tabs-left > .nav-tabs > li,
-.tabs-right > .nav-tabs > li {
- float: none;
-}
-
-.tabs-left > .nav-tabs > li > a,
-.tabs-right > .nav-tabs > li > a {
- min-width: 74px;
- margin-right: 0;
- margin-bottom: 3px;
-}
-
-.tabs-left > .nav-tabs {
- float: left;
- margin-right: 19px;
- border-right: 1px solid #ddd;
-}
-
-.tabs-left > .nav-tabs > li > a {
- margin-right: -1px;
- -webkit-border-radius: 4px 0 0 4px;
- -moz-border-radius: 4px 0 0 4px;
- border-radius: 4px 0 0 4px;
-}
-
-.tabs-left > .nav-tabs > li > a:hover,
-.tabs-left > .nav-tabs > li > a:focus {
- border-color: #eeeeee #dddddd #eeeeee #eeeeee;
-}
-
-.tabs-left > .nav-tabs .active > a,
-.tabs-left > .nav-tabs .active > a:hover,
-.tabs-left > .nav-tabs .active > a:focus {
- border-color: #ddd transparent #ddd #ddd;
- *border-right-color: #ffffff;
-}
-
-.tabs-right > .nav-tabs {
- float: right;
- margin-left: 19px;
- border-left: 1px solid #ddd;
-}
-
-.tabs-right > .nav-tabs > li > a {
- margin-left: -1px;
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
-}
-
-.tabs-right > .nav-tabs > li > a:hover,
-.tabs-right > .nav-tabs > li > a:focus {
- border-color: #eeeeee #eeeeee #eeeeee #dddddd;
-}
-
-.tabs-right > .nav-tabs .active > a,
-.tabs-right > .nav-tabs .active > a:hover,
-.tabs-right > .nav-tabs .active > a:focus {
- border-color: #ddd #ddd #ddd transparent;
- *border-left-color: #ffffff;
-}
-
-.nav > .disabled > a {
- color: #999999;
-}
-
-.nav > .disabled > a:hover,
-.nav > .disabled > a:focus {
- text-decoration: none;
- cursor: default;
- background-color: transparent;
-}
-
-.navbar {
- *position: relative;
- *z-index: 2;
- margin-bottom: 20px;
- overflow: visible;
-}
-
-.navbar-inner {
- min-height: 40px;
- padding-right: 20px;
- padding-left: 20px;
- background-color: #fafafa;
- background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));
- background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2);
- background-image: -o-linear-gradient(top, #ffffff, #f2f2f2);
- background-image: linear-gradient(to bottom, #ffffff, #f2f2f2);
- background-repeat: repeat-x;
- border: 1px solid #d4d4d4;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
- *zoom: 1;
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
-}
-
-.navbar-inner:before,
-.navbar-inner:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.navbar-inner:after {
- clear: both;
-}
-
-.navbar .container {
- width: auto;
-}
-
-.nav-collapse.collapse {
- height: auto;
- overflow: visible;
-}
-
-.navbar .brand {
- display: block;
- float: left;
- padding: 10px 20px 10px;
- margin-left: -20px;
- font-size: 20px;
- font-weight: 200;
- color: #777777;
- text-shadow: 0 1px 0 #ffffff;
-}
-
-.navbar .brand:hover,
-.navbar .brand:focus {
- text-decoration: none;
-}
-
-.navbar-text {
- margin-bottom: 0;
- line-height: 40px;
- color: #777777;
-}
-
-.navbar-link {
- color: #777777;
-}
-
-.navbar-link:hover,
-.navbar-link:focus {
- color: #333333;
-}
-
-.navbar .divider-vertical {
- height: 40px;
- margin: 0 9px;
- border-right: 1px solid #ffffff;
- border-left: 1px solid #f2f2f2;
-}
-
-.navbar .btn,
-.navbar .btn-group {
- margin-top: 5px;
-}
-
-.navbar .btn-group .btn,
-.navbar .input-prepend .btn,
-.navbar .input-append .btn,
-.navbar .input-prepend .btn-group,
-.navbar .input-append .btn-group {
- margin-top: 0;
-}
-
-.navbar-form {
- margin-bottom: 0;
- *zoom: 1;
-}
-
-.navbar-form:before,
-.navbar-form:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.navbar-form:after {
- clear: both;
-}
-
-.navbar-form input,
-.navbar-form select,
-.navbar-form .radio,
-.navbar-form .checkbox {
- margin-top: 5px;
-}
-
-.navbar-form input,
-.navbar-form select,
-.navbar-form .btn {
- display: inline-block;
- margin-bottom: 0;
-}
-
-.navbar-form input[type="image"],
-.navbar-form input[type="checkbox"],
-.navbar-form input[type="radio"] {
- margin-top: 3px;
-}
-
-.navbar-form .input-append,
-.navbar-form .input-prepend {
- margin-top: 5px;
- white-space: nowrap;
-}
-
-.navbar-form .input-append input,
-.navbar-form .input-prepend input {
- margin-top: 0;
-}
-
-.navbar-search {
- position: relative;
- float: left;
- margin-top: 5px;
- margin-bottom: 0;
-}
-
-.navbar-search .search-query {
- padding: 4px 14px;
- margin-bottom: 0;
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-size: 13px;
- font-weight: normal;
- line-height: 1;
- -webkit-border-radius: 15px;
- -moz-border-radius: 15px;
- border-radius: 15px;
-}
-
-.navbar-static-top {
- position: static;
- margin-bottom: 0;
-}
-
-.navbar-static-top .navbar-inner {
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
-}
-
-.navbar-fixed-top,
-.navbar-fixed-bottom {
- position: fixed;
- right: 0;
- left: 0;
- z-index: 1030;
- margin-bottom: 0;
-}
-
-.navbar-fixed-top .navbar-inner,
-.navbar-static-top .navbar-inner {
- border-width: 0 0 1px;
-}
-
-.navbar-fixed-bottom .navbar-inner {
- border-width: 1px 0 0;
-}
-
-.navbar-fixed-top .navbar-inner,
-.navbar-fixed-bottom .navbar-inner {
- padding-right: 0;
- padding-left: 0;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
-}
-
-.navbar-static-top .container,
-.navbar-fixed-top .container,
-.navbar-fixed-bottom .container {
- width: 940px;
-}
-
-.navbar-fixed-top {
- top: 0;
-}
-
-.navbar-fixed-top .navbar-inner,
-.navbar-static-top .navbar-inner {
- -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
- box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
-}
-
-.navbar-fixed-bottom {
- bottom: 0;
-}
-
-.navbar-fixed-bottom .navbar-inner {
- -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
- box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
-}
-
-.navbar .nav {
- position: relative;
- left: 0;
- display: block;
- float: left;
- margin: 0 10px 0 0;
-}
-
-.navbar .nav.pull-right {
- float: right;
- margin-right: 0;
-}
-
-.navbar .nav > li {
- float: left;
-}
-
-.navbar .nav > li > a {
- float: none;
- padding: 10px 15px 10px;
- color: #777777;
- text-decoration: none;
- text-shadow: 0 1px 0 #ffffff;
-}
-
-.navbar .nav .dropdown-toggle .caret {
- margin-top: 8px;
-}
-
-.navbar .nav > li > a:focus,
-.navbar .nav > li > a:hover {
- color: #333333;
- text-decoration: none;
- background-color: transparent;
-}
-
-.navbar .nav > .active > a,
-.navbar .nav > .active > a:hover,
-.navbar .nav > .active > a:focus {
- color: #555555;
- text-decoration: none;
- background-color: #e5e5e5;
- -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
- -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
- box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
-}
-
-.navbar .btn-navbar {
- display: none;
- float: right;
- padding: 7px 10px;
- margin-right: 5px;
- margin-left: 5px;
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #ededed;
- *background-color: #e5e5e5;
- background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));
- background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5);
- background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5);
- background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5);
- background-repeat: repeat-x;
- border-color: #e5e5e5 #e5e5e5 #bfbfbf;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
- -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
- -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
-}
-
-.navbar .btn-navbar:hover,
-.navbar .btn-navbar:focus,
-.navbar .btn-navbar:active,
-.navbar .btn-navbar.active,
-.navbar .btn-navbar.disabled,
-.navbar .btn-navbar[disabled] {
- color: #ffffff;
- background-color: #e5e5e5;
- *background-color: #d9d9d9;
-}
-
-.navbar .btn-navbar:active,
-.navbar .btn-navbar.active {
- background-color: #cccccc \9;
-}
-
-.navbar .btn-navbar .icon-bar {
- display: block;
- width: 18px;
- height: 2px;
- background-color: #f5f5f5;
- -webkit-border-radius: 1px;
- -moz-border-radius: 1px;
- border-radius: 1px;
- -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
- -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
- box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
-}
-
-.btn-navbar .icon-bar + .icon-bar {
- margin-top: 3px;
-}
-
-.navbar .nav > li > .dropdown-menu:before {
- position: absolute;
- top: -7px;
- left: 9px;
- display: inline-block;
- border-right: 7px solid transparent;
- border-bottom: 7px solid #ccc;
- border-left: 7px solid transparent;
- border-bottom-color: rgba(0, 0, 0, 0.2);
- content: '';
-}
-
-.navbar .nav > li > .dropdown-menu:after {
- position: absolute;
- top: -6px;
- left: 10px;
- display: inline-block;
- border-right: 6px solid transparent;
- border-bottom: 6px solid #ffffff;
- border-left: 6px solid transparent;
- content: '';
-}
-
-.navbar-fixed-bottom .nav > li > .dropdown-menu:before {
- top: auto;
- bottom: -7px;
- border-top: 7px solid #ccc;
- border-bottom: 0;
- border-top-color: rgba(0, 0, 0, 0.2);
-}
-
-.navbar-fixed-bottom .nav > li > .dropdown-menu:after {
- top: auto;
- bottom: -6px;
- border-top: 6px solid #ffffff;
- border-bottom: 0;
-}
-
-.navbar .nav li.dropdown > a:hover .caret,
-.navbar .nav li.dropdown > a:focus .caret {
- border-top-color: #333333;
- border-bottom-color: #333333;
-}
-
-.navbar .nav li.dropdown.open > .dropdown-toggle,
-.navbar .nav li.dropdown.active > .dropdown-toggle,
-.navbar .nav li.dropdown.open.active > .dropdown-toggle {
- color: #555555;
- background-color: #e5e5e5;
-}
-
-.navbar .nav li.dropdown > .dropdown-toggle .caret {
- border-top-color: #777777;
- border-bottom-color: #777777;
-}
-
-.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
-.navbar .nav li.dropdown.active > .dropdown-toggle .caret,
-.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret {
- border-top-color: #555555;
- border-bottom-color: #555555;
-}
-
-.navbar .pull-right > li > .dropdown-menu,
-.navbar .nav > li > .dropdown-menu.pull-right {
- right: 0;
- left: auto;
-}
-
-.navbar .pull-right > li > .dropdown-menu:before,
-.navbar .nav > li > .dropdown-menu.pull-right:before {
- right: 12px;
- left: auto;
-}
-
-.navbar .pull-right > li > .dropdown-menu:after,
-.navbar .nav > li > .dropdown-menu.pull-right:after {
- right: 13px;
- left: auto;
-}
-
-.navbar .pull-right > li > .dropdown-menu .dropdown-menu,
-.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu {
- right: 100%;
- left: auto;
- margin-right: -1px;
- margin-left: 0;
- -webkit-border-radius: 6px 0 6px 6px;
- -moz-border-radius: 6px 0 6px 6px;
- border-radius: 6px 0 6px 6px;
-}
-
-.navbar-inverse .navbar-inner {
- background-color: #1b1b1b;
- background-image: -moz-linear-gradient(top, #222222, #111111);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));
- background-image: -webkit-linear-gradient(top, #222222, #111111);
- background-image: -o-linear-gradient(top, #222222, #111111);
- background-image: linear-gradient(to bottom, #222222, #111111);
- background-repeat: repeat-x;
- border-color: #252525;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
-}
-
-.navbar-inverse .brand,
-.navbar-inverse .nav > li > a {
- color: #999999;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
-}
-
-.navbar-inverse .brand:hover,
-.navbar-inverse .nav > li > a:hover,
-.navbar-inverse .brand:focus,
-.navbar-inverse .nav > li > a:focus {
- color: #ffffff;
-}
-
-.navbar-inverse .brand {
- color: #999999;
-}
-
-.navbar-inverse .navbar-text {
- color: #999999;
-}
-
-.navbar-inverse .nav > li > a:focus,
-.navbar-inverse .nav > li > a:hover {
- color: #ffffff;
- background-color: transparent;
-}
-
-.navbar-inverse .nav .active > a,
-.navbar-inverse .nav .active > a:hover,
-.navbar-inverse .nav .active > a:focus {
- color: #ffffff;
- background-color: #111111;
-}
-
-.navbar-inverse .navbar-link {
- color: #999999;
-}
-
-.navbar-inverse .navbar-link:hover,
-.navbar-inverse .navbar-link:focus {
- color: #ffffff;
-}
-
-.navbar-inverse .divider-vertical {
- border-right-color: #222222;
- border-left-color: #111111;
-}
-
-.navbar-inverse .nav li.dropdown.open > .dropdown-toggle,
-.navbar-inverse .nav li.dropdown.active > .dropdown-toggle,
-.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle {
- color: #ffffff;
- background-color: #111111;
-}
-
-.navbar-inverse .nav li.dropdown > a:hover .caret,
-.navbar-inverse .nav li.dropdown > a:focus .caret {
- border-top-color: #ffffff;
- border-bottom-color: #ffffff;
-}
-
-.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret {
- border-top-color: #999999;
- border-bottom-color: #999999;
-}
-
-.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret,
-.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret,
-.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret {
- border-top-color: #ffffff;
- border-bottom-color: #ffffff;
-}
-
-.navbar-inverse .navbar-search .search-query {
- color: #ffffff;
- background-color: #515151;
- border-color: #111111;
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
- -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
- box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
- -webkit-transition: none;
- -moz-transition: none;
- -o-transition: none;
- transition: none;
-}
-
-.navbar-inverse .navbar-search .search-query:-moz-placeholder {
- color: #cccccc;
-}
-
-.navbar-inverse .navbar-search .search-query:-ms-input-placeholder {
- color: #cccccc;
-}
-
-.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder {
- color: #cccccc;
-}
-
-.navbar-inverse .navbar-search .search-query:focus,
-.navbar-inverse .navbar-search .search-query.focused {
- padding: 5px 15px;
- color: #333333;
- text-shadow: 0 1px 0 #ffffff;
- background-color: #ffffff;
- border: 0;
- outline: 0;
- -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
- -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
- box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
-}
-
-.navbar-inverse .btn-navbar {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #0e0e0e;
- *background-color: #040404;
- background-image: -moz-linear-gradient(top, #151515, #040404);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404));
- background-image: -webkit-linear-gradient(top, #151515, #040404);
- background-image: -o-linear-gradient(top, #151515, #040404);
- background-image: linear-gradient(to bottom, #151515, #040404);
- background-repeat: repeat-x;
- border-color: #040404 #040404 #000000;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-}
-
-.navbar-inverse .btn-navbar:hover,
-.navbar-inverse .btn-navbar:focus,
-.navbar-inverse .btn-navbar:active,
-.navbar-inverse .btn-navbar.active,
-.navbar-inverse .btn-navbar.disabled,
-.navbar-inverse .btn-navbar[disabled] {
- color: #ffffff;
- background-color: #040404;
- *background-color: #000000;
-}
-
-.navbar-inverse .btn-navbar:active,
-.navbar-inverse .btn-navbar.active {
- background-color: #000000 \9;
-}
-
-.breadcrumb {
- padding: 8px 15px;
- margin: 0 0 20px;
- list-style: none;
- background-color: #f5f5f5;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-
-.breadcrumb > li {
- display: inline-block;
- *display: inline;
- text-shadow: 0 1px 0 #ffffff;
- *zoom: 1;
-}
-
-.breadcrumb > li > .divider {
- padding: 0 5px;
- color: #ccc;
-}
-
-.breadcrumb > .active {
- color: #999999;
-}
-
-.pagination {
- margin: 20px 0;
-}
-
-.pagination ul {
- display: inline-block;
- *display: inline;
- margin-bottom: 0;
- margin-left: 0;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- *zoom: 1;
- -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-
-.pagination ul > li {
- display: inline;
-}
-
-.pagination ul > li > a,
-.pagination ul > li > span {
- float: left;
- padding: 4px 12px;
- line-height: 20px;
- text-decoration: none;
- background-color: #ffffff;
- border: 1px solid #dddddd;
- border-left-width: 0;
-}
-
-.pagination ul > li > a:hover,
-.pagination ul > li > a:focus,
-.pagination ul > .active > a,
-.pagination ul > .active > span {
- background-color: #f5f5f5;
-}
-
-.pagination ul > .active > a,
-.pagination ul > .active > span {
- color: #999999;
- cursor: default;
-}
-
-.pagination ul > .disabled > span,
-.pagination ul > .disabled > a,
-.pagination ul > .disabled > a:hover,
-.pagination ul > .disabled > a:focus {
- color: #999999;
- cursor: default;
- background-color: transparent;
-}
-
-.pagination ul > li:first-child > a,
-.pagination ul > li:first-child > span {
- border-left-width: 1px;
- -webkit-border-bottom-left-radius: 4px;
- border-bottom-left-radius: 4px;
- -webkit-border-top-left-radius: 4px;
- border-top-left-radius: 4px;
- -moz-border-radius-bottomleft: 4px;
- -moz-border-radius-topleft: 4px;
-}
-
-.pagination ul > li:last-child > a,
-.pagination ul > li:last-child > span {
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
- -webkit-border-bottom-right-radius: 4px;
- border-bottom-right-radius: 4px;
- -moz-border-radius-topright: 4px;
- -moz-border-radius-bottomright: 4px;
-}
-
-.pagination-centered {
- text-align: center;
-}
-
-.pagination-right {
- text-align: right;
-}
-
-.pagination-large ul > li > a,
-.pagination-large ul > li > span {
- padding: 11px 19px;
- font-size: 17.5px;
-}
-
-.pagination-large ul > li:first-child > a,
-.pagination-large ul > li:first-child > span {
- -webkit-border-bottom-left-radius: 6px;
- border-bottom-left-radius: 6px;
- -webkit-border-top-left-radius: 6px;
- border-top-left-radius: 6px;
- -moz-border-radius-bottomleft: 6px;
- -moz-border-radius-topleft: 6px;
-}
-
-.pagination-large ul > li:last-child > a,
-.pagination-large ul > li:last-child > span {
- -webkit-border-top-right-radius: 6px;
- border-top-right-radius: 6px;
- -webkit-border-bottom-right-radius: 6px;
- border-bottom-right-radius: 6px;
- -moz-border-radius-topright: 6px;
- -moz-border-radius-bottomright: 6px;
-}
-
-.pagination-mini ul > li:first-child > a,
-.pagination-small ul > li:first-child > a,
-.pagination-mini ul > li:first-child > span,
-.pagination-small ul > li:first-child > span {
- -webkit-border-bottom-left-radius: 3px;
- border-bottom-left-radius: 3px;
- -webkit-border-top-left-radius: 3px;
- border-top-left-radius: 3px;
- -moz-border-radius-bottomleft: 3px;
- -moz-border-radius-topleft: 3px;
-}
-
-.pagination-mini ul > li:last-child > a,
-.pagination-small ul > li:last-child > a,
-.pagination-mini ul > li:last-child > span,
-.pagination-small ul > li:last-child > span {
- -webkit-border-top-right-radius: 3px;
- border-top-right-radius: 3px;
- -webkit-border-bottom-right-radius: 3px;
- border-bottom-right-radius: 3px;
- -moz-border-radius-topright: 3px;
- -moz-border-radius-bottomright: 3px;
-}
-
-.pagination-small ul > li > a,
-.pagination-small ul > li > span {
- padding: 2px 10px;
- font-size: 11.9px;
-}
-
-.pagination-mini ul > li > a,
-.pagination-mini ul > li > span {
- padding: 0 6px;
- font-size: 10.5px;
-}
-
-.pager {
- margin: 20px 0;
- text-align: center;
- list-style: none;
- *zoom: 1;
-}
-
-.pager:before,
-.pager:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.pager:after {
- clear: both;
-}
-
-.pager li {
- display: inline;
-}
-
-.pager li > a,
-.pager li > span {
- display: inline-block;
- padding: 5px 14px;
- background-color: #fff;
- border: 1px solid #ddd;
- -webkit-border-radius: 15px;
- -moz-border-radius: 15px;
- border-radius: 15px;
-}
-
-.pager li > a:hover,
-.pager li > a:focus {
- text-decoration: none;
- background-color: #f5f5f5;
-}
-
-.pager .next > a,
-.pager .next > span {
- float: right;
-}
-
-.pager .previous > a,
-.pager .previous > span {
- float: left;
-}
-
-.pager .disabled > a,
-.pager .disabled > a:hover,
-.pager .disabled > a:focus,
-.pager .disabled > span {
- color: #999999;
- cursor: default;
- background-color: #fff;
-}
-
-.modal-backdrop {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 1040;
- background-color: #000000;
-}
-
-.modal-backdrop.fade {
- opacity: 0;
-}
-
-.modal-backdrop,
-.modal-backdrop.fade.in {
- opacity: 0.8;
- filter: alpha(opacity=80);
-}
-
-.modal {
- position: fixed;
- top: 10%;
- left: 50%;
- z-index: 1050;
- width: 560px;
- margin-left: -280px;
- background-color: #ffffff;
- border: 1px solid #999;
- border: 1px solid rgba(0, 0, 0, 0.3);
- *border: 1px solid #999;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
- outline: none;
- -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
- -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
- box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding-box;
- background-clip: padding-box;
-}
-
-.modal.fade {
- top: -25%;
- -webkit-transition: opacity 0.3s linear, top 0.3s ease-out;
- -moz-transition: opacity 0.3s linear, top 0.3s ease-out;
- -o-transition: opacity 0.3s linear, top 0.3s ease-out;
- transition: opacity 0.3s linear, top 0.3s ease-out;
-}
-
-.modal.fade.in {
- top: 10%;
-}
-
-.modal-header {
- padding: 9px 15px;
- border-bottom: 1px solid #eee;
-}
-
-.modal-header .close {
- margin-top: 2px;
-}
-
-.modal-header h3 {
- margin: 0;
- line-height: 30px;
-}
-
-.modal-body {
- position: relative;
- max-height: 400px;
- padding: 15px;
- overflow-y: auto;
-}
-
-.modal-form {
- margin-bottom: 0;
-}
-
-.modal-footer {
- padding: 14px 15px 15px;
- margin-bottom: 0;
- text-align: right;
- background-color: #f5f5f5;
- border-top: 1px solid #ddd;
- -webkit-border-radius: 0 0 6px 6px;
- -moz-border-radius: 0 0 6px 6px;
- border-radius: 0 0 6px 6px;
- *zoom: 1;
- -webkit-box-shadow: inset 0 1px 0 #ffffff;
- -moz-box-shadow: inset 0 1px 0 #ffffff;
- box-shadow: inset 0 1px 0 #ffffff;
-}
-
-.modal-footer:before,
-.modal-footer:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.modal-footer:after {
- clear: both;
-}
-
-.modal-footer .btn + .btn {
- margin-bottom: 0;
- margin-left: 5px;
-}
-
-.modal-footer .btn-group .btn + .btn {
- margin-left: -1px;
-}
-
-.modal-footer .btn-block + .btn-block {
- margin-left: 0;
-}
-
-.tooltip {
- position: absolute;
- z-index: 1030;
- display: block;
- font-size: 11px;
- line-height: 1.4;
- opacity: 0;
- filter: alpha(opacity=0);
- visibility: visible;
-}
-
-.tooltip.in {
- opacity: 0.8;
- filter: alpha(opacity=80);
-}
-
-.tooltip.top {
- padding: 5px 0;
- margin-top: -3px;
-}
-
-.tooltip.right {
- padding: 0 5px;
- margin-left: 3px;
-}
-
-.tooltip.bottom {
- padding: 5px 0;
- margin-top: 3px;
-}
-
-.tooltip.left {
- padding: 0 5px;
- margin-left: -3px;
-}
-
-.tooltip-inner {
- max-width: 200px;
- padding: 8px;
- color: #ffffff;
- text-align: center;
- text-decoration: none;
- background-color: #000000;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-
-.tooltip-arrow {
- position: absolute;
- width: 0;
- height: 0;
- border-color: transparent;
- border-style: solid;
-}
-
-.tooltip.top .tooltip-arrow {
- bottom: 0;
- left: 50%;
- margin-left: -5px;
- border-top-color: #000000;
- border-width: 5px 5px 0;
-}
-
-.tooltip.right .tooltip-arrow {
- top: 50%;
- left: 0;
- margin-top: -5px;
- border-right-color: #000000;
- border-width: 5px 5px 5px 0;
-}
-
-.tooltip.left .tooltip-arrow {
- top: 50%;
- right: 0;
- margin-top: -5px;
- border-left-color: #000000;
- border-width: 5px 0 5px 5px;
-}
-
-.tooltip.bottom .tooltip-arrow {
- top: 0;
- left: 50%;
- margin-left: -5px;
- border-bottom-color: #000000;
- border-width: 0 5px 5px;
-}
-
-.popover {
- position: absolute;
- top: 0;
- left: 0;
- z-index: 1010;
- display: none;
- max-width: 276px;
- padding: 1px;
- text-align: left;
- white-space: normal;
- background-color: #ffffff;
- border: 1px solid #ccc;
- border: 1px solid rgba(0, 0, 0, 0.2);
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
- -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding;
- background-clip: padding-box;
-}
-
-.popover.top {
- margin-top: -10px;
-}
-
-.popover.right {
- margin-left: 10px;
-}
-
-.popover.bottom {
- margin-top: 10px;
-}
-
-.popover.left {
- margin-left: -10px;
-}
-
-.popover-title {
- padding: 8px 14px;
- margin: 0;
- font-size: 14px;
- font-weight: normal;
- line-height: 18px;
- background-color: #f7f7f7;
- border-bottom: 1px solid #ebebeb;
- -webkit-border-radius: 5px 5px 0 0;
- -moz-border-radius: 5px 5px 0 0;
- border-radius: 5px 5px 0 0;
-}
-
-.popover-title:empty {
- display: none;
-}
-
-.popover-content {
- padding: 9px 14px;
-}
-
-.popover .arrow,
-.popover .arrow:after {
- position: absolute;
- display: block;
- width: 0;
- height: 0;
- border-color: transparent;
- border-style: solid;
-}
-
-.popover .arrow {
- border-width: 11px;
-}
-
-.popover .arrow:after {
- border-width: 10px;
- content: "";
-}
-
-.popover.top .arrow {
- bottom: -11px;
- left: 50%;
- margin-left: -11px;
- border-top-color: #999;
- border-top-color: rgba(0, 0, 0, 0.25);
- border-bottom-width: 0;
-}
-
-.popover.top .arrow:after {
- bottom: 1px;
- margin-left: -10px;
- border-top-color: #ffffff;
- border-bottom-width: 0;
-}
-
-.popover.right .arrow {
- top: 50%;
- left: -11px;
- margin-top: -11px;
- border-right-color: #999;
- border-right-color: rgba(0, 0, 0, 0.25);
- border-left-width: 0;
-}
-
-.popover.right .arrow:after {
- bottom: -10px;
- left: 1px;
- border-right-color: #ffffff;
- border-left-width: 0;
-}
-
-.popover.bottom .arrow {
- top: -11px;
- left: 50%;
- margin-left: -11px;
- border-bottom-color: #999;
- border-bottom-color: rgba(0, 0, 0, 0.25);
- border-top-width: 0;
-}
-
-.popover.bottom .arrow:after {
- top: 1px;
- margin-left: -10px;
- border-bottom-color: #ffffff;
- border-top-width: 0;
-}
-
-.popover.left .arrow {
- top: 50%;
- right: -11px;
- margin-top: -11px;
- border-left-color: #999;
- border-left-color: rgba(0, 0, 0, 0.25);
- border-right-width: 0;
-}
-
-.popover.left .arrow:after {
- right: 1px;
- bottom: -10px;
- border-left-color: #ffffff;
- border-right-width: 0;
-}
-
-.thumbnails {
- margin-left: -20px;
- list-style: none;
- *zoom: 1;
-}
-
-.thumbnails:before,
-.thumbnails:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.thumbnails:after {
- clear: both;
-}
-
-.row-fluid .thumbnails {
- margin-left: 0;
-}
-
-.thumbnails > li {
- float: left;
- margin-bottom: 20px;
- margin-left: 20px;
-}
-
-.thumbnail {
- display: block;
- padding: 4px;
- line-height: 20px;
- border: 1px solid #ddd;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
- -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
- -webkit-transition: all 0.2s ease-in-out;
- -moz-transition: all 0.2s ease-in-out;
- -o-transition: all 0.2s ease-in-out;
- transition: all 0.2s ease-in-out;
-}
-
-a.thumbnail:hover,
-a.thumbnail:focus {
- border-color: #0088cc;
- -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
- -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
- box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
-}
-
-.thumbnail > img {
- display: block;
- max-width: 100%;
- margin-right: auto;
- margin-left: auto;
-}
-
-.thumbnail .caption {
- padding: 9px;
- color: #555555;
-}
-
-.media,
-.media-body {
- overflow: hidden;
- *overflow: visible;
- zoom: 1;
-}
-
-.media,
-.media .media {
- margin-top: 15px;
-}
-
-.media:first-child {
- margin-top: 0;
-}
-
-.media-object {
- display: block;
-}
-
-.media-heading {
- margin: 0 0 5px;
-}
-
-.media > .pull-left {
- margin-right: 10px;
-}
-
-.media > .pull-right {
- margin-left: 10px;
-}
-
-.media-list {
- margin-left: 0;
- list-style: none;
-}
-
-.label,
-.badge {
- display: inline-block;
- padding: 2px 4px;
- font-size: 11.844px;
- font-weight: bold;
- line-height: 14px;
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- white-space: nowrap;
- vertical-align: baseline;
- background-color: #999999;
-}
-
-.label {
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-
-.badge {
- padding-right: 9px;
- padding-left: 9px;
- -webkit-border-radius: 9px;
- -moz-border-radius: 9px;
- border-radius: 9px;
-}
-
-.label:empty,
-.badge:empty {
- display: none;
-}
-
-a.label:hover,
-a.label:focus,
-a.badge:hover,
-a.badge:focus {
- color: #ffffff;
- text-decoration: none;
- cursor: pointer;
-}
-
-.label-important,
-.badge-important {
- background-color: #b94a48;
-}
-
-.label-important[href],
-.badge-important[href] {
- background-color: #953b39;
-}
-
-.label-warning,
-.badge-warning {
- background-color: #f89406;
-}
-
-.label-warning[href],
-.badge-warning[href] {
- background-color: #c67605;
-}
-
-.label-success,
-.badge-success {
- background-color: #468847;
-}
-
-.label-success[href],
-.badge-success[href] {
- background-color: #356635;
-}
-
-.label-info,
-.badge-info {
- background-color: #3a87ad;
-}
-
-.label-info[href],
-.badge-info[href] {
- background-color: #2d6987;
-}
-
-.label-inverse,
-.badge-inverse {
- background-color: #333333;
-}
-
-.label-inverse[href],
-.badge-inverse[href] {
- background-color: #1a1a1a;
-}
-
-.btn .label,
-.btn .badge {
- position: relative;
- top: -1px;
-}
-
-.btn-mini .label,
-.btn-mini .badge {
- top: 0;
-}
-
-@-webkit-keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
- }
- to {
- background-position: 0 0;
- }
-}
-
-@-moz-keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
- }
- to {
- background-position: 0 0;
- }
-}
-
-@-ms-keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
- }
- to {
- background-position: 0 0;
- }
-}
-
-@-o-keyframes progress-bar-stripes {
- from {
- background-position: 0 0;
- }
- to {
- background-position: 40px 0;
- }
-}
-
-@keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
- }
- to {
- background-position: 0 0;
- }
-}
-
-.progress {
- height: 20px;
- margin-bottom: 20px;
- overflow: hidden;
- background-color: #f7f7f7;
- background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
- background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
- background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
- background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
- background-repeat: repeat-x;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
-}
-
-.progress .bar {
- float: left;
- width: 0;
- height: 100%;
- font-size: 12px;
- color: #ffffff;
- text-align: center;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #0e90d2;
- background-image: -moz-linear-gradient(top, #149bdf, #0480be);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
- background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
- background-image: -o-linear-gradient(top, #149bdf, #0480be);
- background-image: linear-gradient(to bottom, #149bdf, #0480be);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
- -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- -webkit-transition: width 0.6s ease;
- -moz-transition: width 0.6s ease;
- -o-transition: width 0.6s ease;
- transition: width 0.6s ease;
-}
-
-.progress .bar + .bar {
- -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
-}
-
-.progress-striped .bar {
- background-color: #149bdf;
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- -webkit-background-size: 40px 40px;
- -moz-background-size: 40px 40px;
- -o-background-size: 40px 40px;
- background-size: 40px 40px;
-}
-
-.progress.active .bar {
- -webkit-animation: progress-bar-stripes 2s linear infinite;
- -moz-animation: progress-bar-stripes 2s linear infinite;
- -ms-animation: progress-bar-stripes 2s linear infinite;
- -o-animation: progress-bar-stripes 2s linear infinite;
- animation: progress-bar-stripes 2s linear infinite;
-}
-
-.progress-danger .bar,
-.progress .bar-danger {
- background-color: #dd514c;
- background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
- background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
- background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
- background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
-}
-
-.progress-danger.progress-striped .bar,
-.progress-striped .bar-danger {
- background-color: #ee5f5b;
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-}
-
-.progress-success .bar,
-.progress .bar-success {
- background-color: #5eb95e;
- background-image: -moz-linear-gradient(top, #62c462, #57a957);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
- background-image: -webkit-linear-gradient(top, #62c462, #57a957);
- background-image: -o-linear-gradient(top, #62c462, #57a957);
- background-image: linear-gradient(to bottom, #62c462, #57a957);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
-}
-
-.progress-success.progress-striped .bar,
-.progress-striped .bar-success {
- background-color: #62c462;
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-}
-
-.progress-info .bar,
-.progress .bar-info {
- background-color: #4bb1cf;
- background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
- background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
- background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
- background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
-}
-
-.progress-info.progress-striped .bar,
-.progress-striped .bar-info {
- background-color: #5bc0de;
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-}
-
-.progress-warning .bar,
-.progress .bar-warning {
- background-color: #faa732;
- background-image: -moz-linear-gradient(top, #fbb450, #f89406);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
- background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
- background-image: -o-linear-gradient(top, #fbb450, #f89406);
- background-image: linear-gradient(to bottom, #fbb450, #f89406);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
-}
-
-.progress-warning.progress-striped .bar,
-.progress-striped .bar-warning {
- background-color: #fbb450;
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-}
-
-.accordion {
- margin-bottom: 20px;
-}
-
-.accordion-group {
- margin-bottom: 2px;
- border: 1px solid #e5e5e5;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-
-.accordion-heading {
- border-bottom: 0;
-}
-
-.accordion-heading .accordion-toggle {
- display: block;
- padding: 8px 15px;
-}
-
-.accordion-toggle {
- cursor: pointer;
-}
-
-.accordion-inner {
- padding: 9px 15px;
- border-top: 1px solid #e5e5e5;
-}
-
-.carousel {
- position: relative;
- margin-bottom: 20px;
- line-height: 1;
-}
-
-.carousel-inner {
- position: relative;
- width: 100%;
- overflow: hidden;
-}
-
-.carousel-inner > .item {
- position: relative;
- display: none;
- -webkit-transition: 0.6s ease-in-out left;
- -moz-transition: 0.6s ease-in-out left;
- -o-transition: 0.6s ease-in-out left;
- transition: 0.6s ease-in-out left;
-}
-
-.carousel-inner > .item > img,
-.carousel-inner > .item > a > img {
- display: block;
- line-height: 1;
-}
-
-.carousel-inner > .active,
-.carousel-inner > .next,
-.carousel-inner > .prev {
- display: block;
-}
-
-.carousel-inner > .active {
- left: 0;
-}
-
-.carousel-inner > .next,
-.carousel-inner > .prev {
- position: absolute;
- top: 0;
- width: 100%;
-}
-
-.carousel-inner > .next {
- left: 100%;
-}
-
-.carousel-inner > .prev {
- left: -100%;
-}
-
-.carousel-inner > .next.left,
-.carousel-inner > .prev.right {
- left: 0;
-}
-
-.carousel-inner > .active.left {
- left: -100%;
-}
-
-.carousel-inner > .active.right {
- left: 100%;
-}
-
-.carousel-control {
- position: absolute;
- top: 40%;
- left: 15px;
- width: 40px;
- height: 40px;
- margin-top: -20px;
- font-size: 60px;
- font-weight: 100;
- line-height: 30px;
- color: #ffffff;
- text-align: center;
- background: #222222;
- border: 3px solid #ffffff;
- -webkit-border-radius: 23px;
- -moz-border-radius: 23px;
- border-radius: 23px;
- opacity: 0.5;
- filter: alpha(opacity=50);
-}
-
-.carousel-control.right {
- right: 15px;
- left: auto;
-}
-
-.carousel-control:hover,
-.carousel-control:focus {
- color: #ffffff;
- text-decoration: none;
- opacity: 0.9;
- filter: alpha(opacity=90);
-}
-
-.carousel-indicators {
- position: absolute;
- top: 15px;
- right: 15px;
- z-index: 5;
- margin: 0;
- list-style: none;
-}
-
-.carousel-indicators li {
- display: block;
- float: left;
- width: 10px;
- height: 10px;
- margin-left: 5px;
- text-indent: -999px;
- background-color: #ccc;
- background-color: rgba(255, 255, 255, 0.25);
- border-radius: 5px;
-}
-
-.carousel-indicators .active {
- background-color: #fff;
-}
-
-.carousel-caption {
- position: absolute;
- right: 0;
- bottom: 0;
- left: 0;
- padding: 15px;
- background: #333333;
- background: rgba(0, 0, 0, 0.75);
-}
-
-.carousel-caption h4,
-.carousel-caption p {
- line-height: 20px;
- color: #ffffff;
-}
-
-.carousel-caption h4 {
- margin: 0 0 5px;
-}
-
-.carousel-caption p {
- margin-bottom: 0;
-}
-
-.hero-unit {
- padding: 60px;
- margin-bottom: 30px;
- font-size: 18px;
- font-weight: 200;
- line-height: 30px;
- color: inherit;
- background-color: #eeeeee;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
-}
-
-.hero-unit h1 {
- margin-bottom: 0;
- font-size: 60px;
- line-height: 1;
- letter-spacing: -1px;
- color: inherit;
-}
-
-.hero-unit li {
- line-height: 30px;
-}
-
-.pull-right {
- float: right;
-}
-
-.pull-left {
- float: left;
-}
-
-.hide {
- display: none;
-}
-
-.show {
- display: block;
-}
-
-.invisible {
- visibility: hidden;
-}
-
-.affix {
- position: fixed;
-}
diff --git a/framework/yii/bootstrap/assets/css/bootstrap.min.css b/framework/yii/bootstrap/assets/css/bootstrap.min.css
deleted file mode 100644
index b6428e6..0000000
--- a/framework/yii/bootstrap/assets/css/bootstrap.min.css
+++ /dev/null
@@ -1,9 +0,0 @@
-/*!
- * Bootstrap v2.3.2
- *
- * Copyright 2012 Twitter, Inc
- * Licensed under the Apache License v2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Designed and built with all the love in the world @twitter by @mdo and @fat.
- */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover,a:focus{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}a.muted:hover,a.muted:focus{color:#808080}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover,a.text-error:focus{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;padding-right:5px;padding-left:5px;*zoom:1}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success>td{background-color:#dff0d8}.table tbody tr.error>td{background-color:#f2dede}.table tbody tr.warning>td{background-color:#fcf8e3}.table tbody tr.info>td{background-color:#d9edf7}.table-hover tbody tr.success:hover>td{background-color:#d0e9c6}.table-hover tbody tr.error:hover>td{background-color:#ebcccc}.table-hover tbody tr.warning:hover>td{background-color:#faf2cc}.table-hover tbody tr.info:hover>td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{width:16px;background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover,.btn:focus{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover,.btn-link:focus{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover,.navbar-link:focus{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#fff}.navbar-inverse .brand{color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-title:empty{display:none}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}
diff --git a/framework/yii/bootstrap/assets/img/glyphicons-halflings-white.png b/framework/yii/bootstrap/assets/img/glyphicons-halflings-white.png
deleted file mode 100644
index 3bf6484..0000000
Binary files a/framework/yii/bootstrap/assets/img/glyphicons-halflings-white.png and /dev/null differ
diff --git a/framework/yii/bootstrap/assets/img/glyphicons-halflings.png b/framework/yii/bootstrap/assets/img/glyphicons-halflings.png
deleted file mode 100644
index a996999..0000000
Binary files a/framework/yii/bootstrap/assets/img/glyphicons-halflings.png and /dev/null differ
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-affix.js b/framework/yii/bootstrap/assets/js/bootstrap-affix.js
deleted file mode 100644
index 91c9ced..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-affix.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/* ==========================================================
- * bootstrap-affix.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#affix
- * ==========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ========================================================== */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* AFFIX CLASS DEFINITION
- * ====================== */
-
- var Affix = function (element, options) {
- this.options = $.extend({}, $.fn.affix.defaults, options)
- this.$window = $(window)
- .on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
- .on('click.affix.data-api', $.proxy(function () { setTimeout($.proxy(this.checkPosition, this), 1) }, this))
- this.$element = $(element)
- this.checkPosition()
- }
-
- Affix.prototype.checkPosition = function () {
- if (!this.$element.is(':visible')) return
-
- var scrollHeight = $(document).height()
- , scrollTop = this.$window.scrollTop()
- , position = this.$element.offset()
- , offset = this.options.offset
- , offsetBottom = offset.bottom
- , offsetTop = offset.top
- , reset = 'affix affix-top affix-bottom'
- , affix
-
- if (typeof offset != 'object') offsetBottom = offsetTop = offset
- if (typeof offsetTop == 'function') offsetTop = offset.top()
- if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
-
- affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
- false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
- 'bottom' : offsetTop != null && scrollTop <= offsetTop ?
- 'top' : false
-
- if (this.affixed === affix) return
-
- this.affixed = affix
- this.unpin = affix == 'bottom' ? position.top - scrollTop : null
-
- this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
- }
-
-
- /* AFFIX PLUGIN DEFINITION
- * ======================= */
-
- var old = $.fn.affix
-
- $.fn.affix = function (option) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('affix')
- , options = typeof option == 'object' && option
- if (!data) $this.data('affix', (data = new Affix(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.affix.Constructor = Affix
-
- $.fn.affix.defaults = {
- offset: 0
- }
-
-
- /* AFFIX NO CONFLICT
- * ================= */
-
- $.fn.affix.noConflict = function () {
- $.fn.affix = old
- return this
- }
-
-
- /* AFFIX DATA-API
- * ============== */
-
- $(window).on('load', function () {
- $('[data-spy="affix"]').each(function () {
- var $spy = $(this)
- , data = $spy.data()
-
- data.offset = data.offset || {}
-
- data.offsetBottom && (data.offset.bottom = data.offsetBottom)
- data.offsetTop && (data.offset.top = data.offsetTop)
-
- $spy.affix(data)
- })
- })
-
-
-}(window.jQuery);
\ No newline at end of file
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-alert.js b/framework/yii/bootstrap/assets/js/bootstrap-alert.js
deleted file mode 100644
index 0cefe5f..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-alert.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/* ==========================================================
- * bootstrap-alert.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#alerts
- * ==========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ========================================================== */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* ALERT CLASS DEFINITION
- * ====================== */
-
- var dismiss = '[data-dismiss="alert"]'
- , Alert = function (el) {
- $(el).on('click', dismiss, this.close)
- }
-
- Alert.prototype.close = function (e) {
- var $this = $(this)
- , selector = $this.attr('data-target')
- , $parent
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- $parent = $(selector)
-
- e && e.preventDefault()
-
- $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
-
- $parent.trigger(e = $.Event('close'))
-
- if (e.isDefaultPrevented()) return
-
- $parent.removeClass('in')
-
- function removeElement() {
- $parent
- .trigger('closed')
- .remove()
- }
-
- $.support.transition && $parent.hasClass('fade') ?
- $parent.on($.support.transition.end, removeElement) :
- removeElement()
- }
-
-
- /* ALERT PLUGIN DEFINITION
- * ======================= */
-
- var old = $.fn.alert
-
- $.fn.alert = function (option) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('alert')
- if (!data) $this.data('alert', (data = new Alert(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
-
- $.fn.alert.Constructor = Alert
-
-
- /* ALERT NO CONFLICT
- * ================= */
-
- $.fn.alert.noConflict = function () {
- $.fn.alert = old
- return this
- }
-
-
- /* ALERT DATA-API
- * ============== */
-
- $(document).on('click.alert.data-api', dismiss, Alert.prototype.close)
-
-}(window.jQuery);
\ No newline at end of file
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-button.js b/framework/yii/bootstrap/assets/js/bootstrap-button.js
deleted file mode 100644
index ce45991..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-button.js
+++ /dev/null
@@ -1,105 +0,0 @@
-/* ============================================================
- * bootstrap-button.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#buttons
- * ============================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============================================================ */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* BUTTON PUBLIC CLASS DEFINITION
- * ============================== */
-
- var Button = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, $.fn.button.defaults, options)
- }
-
- Button.prototype.setState = function (state) {
- var d = 'disabled'
- , $el = this.$element
- , data = $el.data()
- , val = $el.is('input') ? 'val' : 'html'
-
- state = state + 'Text'
- data.resetText || $el.data('resetText', $el[val]())
-
- $el[val](data[state] || this.options[state])
-
- // push to event loop to allow forms to submit
- setTimeout(function () {
- state == 'loadingText' ?
- $el.addClass(d).attr(d, d) :
- $el.removeClass(d).removeAttr(d)
- }, 0)
- }
-
- Button.prototype.toggle = function () {
- var $parent = this.$element.closest('[data-toggle="buttons-radio"]')
-
- $parent && $parent
- .find('.active')
- .removeClass('active')
-
- this.$element.toggleClass('active')
- }
-
-
- /* BUTTON PLUGIN DEFINITION
- * ======================== */
-
- var old = $.fn.button
-
- $.fn.button = function (option) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('button')
- , options = typeof option == 'object' && option
- if (!data) $this.data('button', (data = new Button(this, options)))
- if (option == 'toggle') data.toggle()
- else if (option) data.setState(option)
- })
- }
-
- $.fn.button.defaults = {
- loadingText: 'loading...'
- }
-
- $.fn.button.Constructor = Button
-
-
- /* BUTTON NO CONFLICT
- * ================== */
-
- $.fn.button.noConflict = function () {
- $.fn.button = old
- return this
- }
-
-
- /* BUTTON DATA-API
- * =============== */
-
- $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) {
- var $btn = $(e.target)
- if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
- $btn.button('toggle')
- })
-
-}(window.jQuery);
\ No newline at end of file
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-carousel.js b/framework/yii/bootstrap/assets/js/bootstrap-carousel.js
deleted file mode 100644
index 476494a..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-carousel.js
+++ /dev/null
@@ -1,207 +0,0 @@
-/* ==========================================================
- * bootstrap-carousel.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#carousel
- * ==========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ========================================================== */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* CAROUSEL CLASS DEFINITION
- * ========================= */
-
- var Carousel = function (element, options) {
- this.$element = $(element)
- this.$indicators = this.$element.find('.carousel-indicators')
- this.options = options
- this.options.pause == 'hover' && this.$element
- .on('mouseenter', $.proxy(this.pause, this))
- .on('mouseleave', $.proxy(this.cycle, this))
- }
-
- Carousel.prototype = {
-
- cycle: function (e) {
- if (!e) this.paused = false
- if (this.interval) clearInterval(this.interval);
- this.options.interval
- && !this.paused
- && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
- return this
- }
-
- , getActiveIndex: function () {
- this.$active = this.$element.find('.item.active')
- this.$items = this.$active.parent().children()
- return this.$items.index(this.$active)
- }
-
- , to: function (pos) {
- var activeIndex = this.getActiveIndex()
- , that = this
-
- if (pos > (this.$items.length - 1) || pos < 0) return
-
- if (this.sliding) {
- return this.$element.one('slid', function () {
- that.to(pos)
- })
- }
-
- if (activeIndex == pos) {
- return this.pause().cycle()
- }
-
- return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
- }
-
- , pause: function (e) {
- if (!e) this.paused = true
- if (this.$element.find('.next, .prev').length && $.support.transition.end) {
- this.$element.trigger($.support.transition.end)
- this.cycle(true)
- }
- clearInterval(this.interval)
- this.interval = null
- return this
- }
-
- , next: function () {
- if (this.sliding) return
- return this.slide('next')
- }
-
- , prev: function () {
- if (this.sliding) return
- return this.slide('prev')
- }
-
- , slide: function (type, next) {
- var $active = this.$element.find('.item.active')
- , $next = next || $active[type]()
- , isCycling = this.interval
- , direction = type == 'next' ? 'left' : 'right'
- , fallback = type == 'next' ? 'first' : 'last'
- , that = this
- , e
-
- this.sliding = true
-
- isCycling && this.pause()
-
- $next = $next.length ? $next : this.$element.find('.item')[fallback]()
-
- e = $.Event('slide', {
- relatedTarget: $next[0]
- , direction: direction
- })
-
- if ($next.hasClass('active')) return
-
- if (this.$indicators.length) {
- this.$indicators.find('.active').removeClass('active')
- this.$element.one('slid', function () {
- var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
- $nextIndicator && $nextIndicator.addClass('active')
- })
- }
-
- if ($.support.transition && this.$element.hasClass('slide')) {
- this.$element.trigger(e)
- if (e.isDefaultPrevented()) return
- $next.addClass(type)
- $next[0].offsetWidth // force reflow
- $active.addClass(direction)
- $next.addClass(direction)
- this.$element.one($.support.transition.end, function () {
- $next.removeClass([type, direction].join(' ')).addClass('active')
- $active.removeClass(['active', direction].join(' '))
- that.sliding = false
- setTimeout(function () { that.$element.trigger('slid') }, 0)
- })
- } else {
- this.$element.trigger(e)
- if (e.isDefaultPrevented()) return
- $active.removeClass('active')
- $next.addClass('active')
- this.sliding = false
- this.$element.trigger('slid')
- }
-
- isCycling && this.cycle()
-
- return this
- }
-
- }
-
-
- /* CAROUSEL PLUGIN DEFINITION
- * ========================== */
-
- var old = $.fn.carousel
-
- $.fn.carousel = function (option) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('carousel')
- , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
- , action = typeof option == 'string' ? option : options.slide
- if (!data) $this.data('carousel', (data = new Carousel(this, options)))
- if (typeof option == 'number') data.to(option)
- else if (action) data[action]()
- else if (options.interval) data.pause().cycle()
- })
- }
-
- $.fn.carousel.defaults = {
- interval: 5000
- , pause: 'hover'
- }
-
- $.fn.carousel.Constructor = Carousel
-
-
- /* CAROUSEL NO CONFLICT
- * ==================== */
-
- $.fn.carousel.noConflict = function () {
- $.fn.carousel = old
- return this
- }
-
- /* CAROUSEL DATA-API
- * ================= */
-
- $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
- var $this = $(this), href
- , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- , options = $.extend({}, $target.data(), $this.data())
- , slideIndex
-
- $target.carousel(options)
-
- if (slideIndex = $this.attr('data-slide-to')) {
- $target.data('carousel').pause().to(slideIndex).cycle()
- }
-
- e.preventDefault()
- })
-
-}(window.jQuery);
\ No newline at end of file
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-collapse.js b/framework/yii/bootstrap/assets/js/bootstrap-collapse.js
deleted file mode 100644
index 74a73a8..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-collapse.js
+++ /dev/null
@@ -1,167 +0,0 @@
-/* =============================================================
- * bootstrap-collapse.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#collapse
- * =============================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============================================================ */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* COLLAPSE PUBLIC CLASS DEFINITION
- * ================================ */
-
- var Collapse = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, $.fn.collapse.defaults, options)
-
- if (this.options.parent) {
- this.$parent = $(this.options.parent)
- }
-
- this.options.toggle && this.toggle()
- }
-
- Collapse.prototype = {
-
- constructor: Collapse
-
- , dimension: function () {
- var hasWidth = this.$element.hasClass('width')
- return hasWidth ? 'width' : 'height'
- }
-
- , show: function () {
- var dimension
- , scroll
- , actives
- , hasData
-
- if (this.transitioning || this.$element.hasClass('in')) return
-
- dimension = this.dimension()
- scroll = $.camelCase(['scroll', dimension].join('-'))
- actives = this.$parent && this.$parent.find('> .accordion-group > .in')
-
- if (actives && actives.length) {
- hasData = actives.data('collapse')
- if (hasData && hasData.transitioning) return
- actives.collapse('hide')
- hasData || actives.data('collapse', null)
- }
-
- this.$element[dimension](0)
- this.transition('addClass', $.Event('show'), 'shown')
- $.support.transition && this.$element[dimension](this.$element[0][scroll])
- }
-
- , hide: function () {
- var dimension
- if (this.transitioning || !this.$element.hasClass('in')) return
- dimension = this.dimension()
- this.reset(this.$element[dimension]())
- this.transition('removeClass', $.Event('hide'), 'hidden')
- this.$element[dimension](0)
- }
-
- , reset: function (size) {
- var dimension = this.dimension()
-
- this.$element
- .removeClass('collapse')
- [dimension](size || 'auto')
- [0].offsetWidth
-
- this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
-
- return this
- }
-
- , transition: function (method, startEvent, completeEvent) {
- var that = this
- , complete = function () {
- if (startEvent.type == 'show') that.reset()
- that.transitioning = 0
- that.$element.trigger(completeEvent)
- }
-
- this.$element.trigger(startEvent)
-
- if (startEvent.isDefaultPrevented()) return
-
- this.transitioning = 1
-
- this.$element[method]('in')
-
- $.support.transition && this.$element.hasClass('collapse') ?
- this.$element.one($.support.transition.end, complete) :
- complete()
- }
-
- , toggle: function () {
- this[this.$element.hasClass('in') ? 'hide' : 'show']()
- }
-
- }
-
-
- /* COLLAPSE PLUGIN DEFINITION
- * ========================== */
-
- var old = $.fn.collapse
-
- $.fn.collapse = function (option) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('collapse')
- , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option)
- if (!data) $this.data('collapse', (data = new Collapse(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.collapse.defaults = {
- toggle: true
- }
-
- $.fn.collapse.Constructor = Collapse
-
-
- /* COLLAPSE NO CONFLICT
- * ==================== */
-
- $.fn.collapse.noConflict = function () {
- $.fn.collapse = old
- return this
- }
-
-
- /* COLLAPSE DATA-API
- * ================= */
-
- $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) {
- var $this = $(this), href
- , target = $this.attr('data-target')
- || e.preventDefault()
- || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
- , option = $(target).data('collapse') ? 'toggle' : $this.data()
- $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
- $(target).collapse(option)
- })
-
-}(window.jQuery);
\ No newline at end of file
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-dropdown.js b/framework/yii/bootstrap/assets/js/bootstrap-dropdown.js
deleted file mode 100644
index 6cc1221..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-dropdown.js
+++ /dev/null
@@ -1,169 +0,0 @@
-/* ============================================================
- * bootstrap-dropdown.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#dropdowns
- * ============================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============================================================ */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* DROPDOWN CLASS DEFINITION
- * ========================= */
-
- var toggle = '[data-toggle=dropdown]'
- , Dropdown = function (element) {
- var $el = $(element).on('click.dropdown.data-api', this.toggle)
- $('html').on('click.dropdown.data-api', function () {
- $el.parent().removeClass('open')
- })
- }
-
- Dropdown.prototype = {
-
- constructor: Dropdown
-
- , toggle: function (e) {
- var $this = $(this)
- , $parent
- , isActive
-
- if ($this.is('.disabled, :disabled')) return
-
- $parent = getParent($this)
-
- isActive = $parent.hasClass('open')
-
- clearMenus()
-
- if (!isActive) {
- if ('ontouchstart' in document.documentElement) {
- // if mobile we we use a backdrop because click events don't delegate
- $('
').insertBefore($(this)).on('click', clearMenus)
- }
- $parent.toggleClass('open')
- }
-
- $this.focus()
-
- return false
- }
-
- , keydown: function (e) {
- var $this
- , $items
- , $active
- , $parent
- , isActive
- , index
-
- if (!/(38|40|27)/.test(e.keyCode)) return
-
- $this = $(this)
-
- e.preventDefault()
- e.stopPropagation()
-
- if ($this.is('.disabled, :disabled')) return
-
- $parent = getParent($this)
-
- isActive = $parent.hasClass('open')
-
- if (!isActive || (isActive && e.keyCode == 27)) {
- if (e.which == 27) $parent.find(toggle).focus()
- return $this.click()
- }
-
- $items = $('[role=menu] li:not(.divider):visible a', $parent)
-
- if (!$items.length) return
-
- index = $items.index($items.filter(':focus'))
-
- if (e.keyCode == 38 && index > 0) index-- // up
- if (e.keyCode == 40 && index < $items.length - 1) index++ // down
- if (!~index) index = 0
-
- $items
- .eq(index)
- .focus()
- }
-
- }
-
- function clearMenus() {
- $('.dropdown-backdrop').remove()
- $(toggle).each(function () {
- getParent($(this)).removeClass('open')
- })
- }
-
- function getParent($this) {
- var selector = $this.attr('data-target')
- , $parent
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- $parent = selector && $(selector)
-
- if (!$parent || !$parent.length) $parent = $this.parent()
-
- return $parent
- }
-
-
- /* DROPDOWN PLUGIN DEFINITION
- * ========================== */
-
- var old = $.fn.dropdown
-
- $.fn.dropdown = function (option) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('dropdown')
- if (!data) $this.data('dropdown', (data = new Dropdown(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
-
- $.fn.dropdown.Constructor = Dropdown
-
-
- /* DROPDOWN NO CONFLICT
- * ==================== */
-
- $.fn.dropdown.noConflict = function () {
- $.fn.dropdown = old
- return this
- }
-
-
- /* APPLY TO STANDARD DROPDOWN ELEMENTS
- * =================================== */
-
- $(document)
- .on('click.dropdown.data-api', clearMenus)
- .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
- .on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
- .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
-
-}(window.jQuery);
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-modal.js b/framework/yii/bootstrap/assets/js/bootstrap-modal.js
deleted file mode 100644
index c3648d8..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-modal.js
+++ /dev/null
@@ -1,247 +0,0 @@
-/* =========================================================
- * bootstrap-modal.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#modals
- * =========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ========================================================= */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* MODAL CLASS DEFINITION
- * ====================== */
-
- var Modal = function (element, options) {
- this.options = options
- this.$element = $(element)
- .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
- this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
- }
-
- Modal.prototype = {
-
- constructor: Modal
-
- , toggle: function () {
- return this[!this.isShown ? 'show' : 'hide']()
- }
-
- , show: function () {
- var that = this
- , e = $.Event('show')
-
- this.$element.trigger(e)
-
- if (this.isShown || e.isDefaultPrevented()) return
-
- this.isShown = true
-
- this.escape()
-
- this.backdrop(function () {
- var transition = $.support.transition && that.$element.hasClass('fade')
-
- if (!that.$element.parent().length) {
- that.$element.appendTo(document.body) //don't move modals dom position
- }
-
- that.$element.show()
-
- if (transition) {
- that.$element[0].offsetWidth // force reflow
- }
-
- that.$element
- .addClass('in')
- .attr('aria-hidden', false)
-
- that.enforceFocus()
-
- transition ?
- that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) :
- that.$element.focus().trigger('shown')
-
- })
- }
-
- , hide: function (e) {
- e && e.preventDefault()
-
- var that = this
-
- e = $.Event('hide')
-
- this.$element.trigger(e)
-
- if (!this.isShown || e.isDefaultPrevented()) return
-
- this.isShown = false
-
- this.escape()
-
- $(document).off('focusin.modal')
-
- this.$element
- .removeClass('in')
- .attr('aria-hidden', true)
-
- $.support.transition && this.$element.hasClass('fade') ?
- this.hideWithTransition() :
- this.hideModal()
- }
-
- , enforceFocus: function () {
- var that = this
- $(document).on('focusin.modal', function (e) {
- if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
- that.$element.focus()
- }
- })
- }
-
- , escape: function () {
- var that = this
- if (this.isShown && this.options.keyboard) {
- this.$element.on('keyup.dismiss.modal', function ( e ) {
- e.which == 27 && that.hide()
- })
- } else if (!this.isShown) {
- this.$element.off('keyup.dismiss.modal')
- }
- }
-
- , hideWithTransition: function () {
- var that = this
- , timeout = setTimeout(function () {
- that.$element.off($.support.transition.end)
- that.hideModal()
- }, 500)
-
- this.$element.one($.support.transition.end, function () {
- clearTimeout(timeout)
- that.hideModal()
- })
- }
-
- , hideModal: function () {
- var that = this
- this.$element.hide()
- this.backdrop(function () {
- that.removeBackdrop()
- that.$element.trigger('hidden')
- })
- }
-
- , removeBackdrop: function () {
- this.$backdrop && this.$backdrop.remove()
- this.$backdrop = null
- }
-
- , backdrop: function (callback) {
- var that = this
- , animate = this.$element.hasClass('fade') ? 'fade' : ''
-
- if (this.isShown && this.options.backdrop) {
- var doAnimate = $.support.transition && animate
-
- this.$backdrop = $('
')
- .appendTo(document.body)
-
- this.$backdrop.click(
- this.options.backdrop == 'static' ?
- $.proxy(this.$element[0].focus, this.$element[0])
- : $.proxy(this.hide, this)
- )
-
- if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
-
- this.$backdrop.addClass('in')
-
- if (!callback) return
-
- doAnimate ?
- this.$backdrop.one($.support.transition.end, callback) :
- callback()
-
- } else if (!this.isShown && this.$backdrop) {
- this.$backdrop.removeClass('in')
-
- $.support.transition && this.$element.hasClass('fade')?
- this.$backdrop.one($.support.transition.end, callback) :
- callback()
-
- } else if (callback) {
- callback()
- }
- }
- }
-
-
- /* MODAL PLUGIN DEFINITION
- * ======================= */
-
- var old = $.fn.modal
-
- $.fn.modal = function (option) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('modal')
- , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
- if (!data) $this.data('modal', (data = new Modal(this, options)))
- if (typeof option == 'string') data[option]()
- else if (options.show) data.show()
- })
- }
-
- $.fn.modal.defaults = {
- backdrop: true
- , keyboard: true
- , show: true
- }
-
- $.fn.modal.Constructor = Modal
-
-
- /* MODAL NO CONFLICT
- * ================= */
-
- $.fn.modal.noConflict = function () {
- $.fn.modal = old
- return this
- }
-
-
- /* MODAL DATA-API
- * ============== */
-
- $(document).on('click.modal.data-api', '[data-toggle="modal"]', function (e) {
- var $this = $(this)
- , href = $this.attr('href')
- , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
- , option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data())
-
- e.preventDefault()
-
- $target
- .modal(option)
- .one('hide', function () {
- $this.focus()
- })
- })
-
-}(window.jQuery);
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-popover.js b/framework/yii/bootstrap/assets/js/bootstrap-popover.js
deleted file mode 100644
index e6a23d2..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-popover.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/* ===========================================================
- * bootstrap-popover.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#popovers
- * ===========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * =========================================================== */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* POPOVER PUBLIC CLASS DEFINITION
- * =============================== */
-
- var Popover = function (element, options) {
- this.init('popover', element, options)
- }
-
-
- /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
- ========================================== */
-
- Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
-
- constructor: Popover
-
- , setContent: function () {
- var $tip = this.tip()
- , title = this.getTitle()
- , content = this.getContent()
-
- $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
- $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
-
- $tip.removeClass('fade top bottom left right in')
- }
-
- , hasContent: function () {
- return this.getTitle() || this.getContent()
- }
-
- , getContent: function () {
- var content
- , $e = this.$element
- , o = this.options
-
- content = (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
- || $e.attr('data-content')
-
- return content
- }
-
- , tip: function () {
- if (!this.$tip) {
- this.$tip = $(this.options.template)
- }
- return this.$tip
- }
-
- , destroy: function () {
- this.hide().$element.off('.' + this.type).removeData(this.type)
- }
-
- })
-
-
- /* POPOVER PLUGIN DEFINITION
- * ======================= */
-
- var old = $.fn.popover
-
- $.fn.popover = function (option) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('popover')
- , options = typeof option == 'object' && option
- if (!data) $this.data('popover', (data = new Popover(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.popover.Constructor = Popover
-
- $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
- placement: 'right'
- , trigger: 'click'
- , content: ''
- , template: ''
- })
-
-
- /* POPOVER NO CONFLICT
- * =================== */
-
- $.fn.popover.noConflict = function () {
- $.fn.popover = old
- return this
- }
-
-}(window.jQuery);
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-scrollspy.js b/framework/yii/bootstrap/assets/js/bootstrap-scrollspy.js
deleted file mode 100644
index 7dd60c4..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-scrollspy.js
+++ /dev/null
@@ -1,162 +0,0 @@
-/* =============================================================
- * bootstrap-scrollspy.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#scrollspy
- * =============================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============================================================== */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* SCROLLSPY CLASS DEFINITION
- * ========================== */
-
- function ScrollSpy(element, options) {
- var process = $.proxy(this.process, this)
- , $element = $(element).is('body') ? $(window) : $(element)
- , href
- this.options = $.extend({}, $.fn.scrollspy.defaults, options)
- this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process)
- this.selector = (this.options.target
- || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- || '') + ' .nav li > a'
- this.$body = $('body')
- this.refresh()
- this.process()
- }
-
- ScrollSpy.prototype = {
-
- constructor: ScrollSpy
-
- , refresh: function () {
- var self = this
- , $targets
-
- this.offsets = $([])
- this.targets = $([])
-
- $targets = this.$body
- .find(this.selector)
- .map(function () {
- var $el = $(this)
- , href = $el.data('target') || $el.attr('href')
- , $href = /^#\w/.test(href) && $(href)
- return ( $href
- && $href.length
- && [[ $href.position().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]] ) || null
- })
- .sort(function (a, b) { return a[0] - b[0] })
- .each(function () {
- self.offsets.push(this[0])
- self.targets.push(this[1])
- })
- }
-
- , process: function () {
- var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
- , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
- , maxScroll = scrollHeight - this.$scrollElement.height()
- , offsets = this.offsets
- , targets = this.targets
- , activeTarget = this.activeTarget
- , i
-
- if (scrollTop >= maxScroll) {
- return activeTarget != (i = targets.last()[0])
- && this.activate ( i )
- }
-
- for (i = offsets.length; i--;) {
- activeTarget != targets[i]
- && scrollTop >= offsets[i]
- && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
- && this.activate( targets[i] )
- }
- }
-
- , activate: function (target) {
- var active
- , selector
-
- this.activeTarget = target
-
- $(this.selector)
- .parent('.active')
- .removeClass('active')
-
- selector = this.selector
- + '[data-target="' + target + '"],'
- + this.selector + '[href="' + target + '"]'
-
- active = $(selector)
- .parent('li')
- .addClass('active')
-
- if (active.parent('.dropdown-menu').length) {
- active = active.closest('li.dropdown').addClass('active')
- }
-
- active.trigger('activate')
- }
-
- }
-
-
- /* SCROLLSPY PLUGIN DEFINITION
- * =========================== */
-
- var old = $.fn.scrollspy
-
- $.fn.scrollspy = function (option) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('scrollspy')
- , options = typeof option == 'object' && option
- if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.scrollspy.Constructor = ScrollSpy
-
- $.fn.scrollspy.defaults = {
- offset: 10
- }
-
-
- /* SCROLLSPY NO CONFLICT
- * ===================== */
-
- $.fn.scrollspy.noConflict = function () {
- $.fn.scrollspy = old
- return this
- }
-
-
- /* SCROLLSPY DATA-API
- * ================== */
-
- $(window).on('load', function () {
- $('[data-spy="scroll"]').each(function () {
- var $spy = $(this)
- $spy.scrollspy($spy.data())
- })
- })
-
-}(window.jQuery);
\ No newline at end of file
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-tab.js b/framework/yii/bootstrap/assets/js/bootstrap-tab.js
deleted file mode 100644
index a516151..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-tab.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/* ========================================================
- * bootstrap-tab.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#tabs
- * ========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================== */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* TAB CLASS DEFINITION
- * ==================== */
-
- var Tab = function (element) {
- this.element = $(element)
- }
-
- Tab.prototype = {
-
- constructor: Tab
-
- , show: function () {
- var $this = this.element
- , $ul = $this.closest('ul:not(.dropdown-menu)')
- , selector = $this.attr('data-target')
- , previous
- , $target
- , e
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- if ( $this.parent('li').hasClass('active') ) return
-
- previous = $ul.find('.active:last a')[0]
-
- e = $.Event('show', {
- relatedTarget: previous
- })
-
- $this.trigger(e)
-
- if (e.isDefaultPrevented()) return
-
- $target = $(selector)
-
- this.activate($this.parent('li'), $ul)
- this.activate($target, $target.parent(), function () {
- $this.trigger({
- type: 'shown'
- , relatedTarget: previous
- })
- })
- }
-
- , activate: function ( element, container, callback) {
- var $active = container.find('> .active')
- , transition = callback
- && $.support.transition
- && $active.hasClass('fade')
-
- function next() {
- $active
- .removeClass('active')
- .find('> .dropdown-menu > .active')
- .removeClass('active')
-
- element.addClass('active')
-
- if (transition) {
- element[0].offsetWidth // reflow for transition
- element.addClass('in')
- } else {
- element.removeClass('fade')
- }
-
- if ( element.parent('.dropdown-menu') ) {
- element.closest('li.dropdown').addClass('active')
- }
-
- callback && callback()
- }
-
- transition ?
- $active.one($.support.transition.end, next) :
- next()
-
- $active.removeClass('in')
- }
- }
-
-
- /* TAB PLUGIN DEFINITION
- * ===================== */
-
- var old = $.fn.tab
-
- $.fn.tab = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('tab')
- if (!data) $this.data('tab', (data = new Tab(this)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.tab.Constructor = Tab
-
-
- /* TAB NO CONFLICT
- * =============== */
-
- $.fn.tab.noConflict = function () {
- $.fn.tab = old
- return this
- }
-
-
- /* TAB DATA-API
- * ============ */
-
- $(document).on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
- e.preventDefault()
- $(this).tab('show')
- })
-
-}(window.jQuery);
\ No newline at end of file
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-tooltip.js b/framework/yii/bootstrap/assets/js/bootstrap-tooltip.js
deleted file mode 100644
index a3bbd58..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-tooltip.js
+++ /dev/null
@@ -1,361 +0,0 @@
-/* ===========================================================
- * bootstrap-tooltip.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#tooltips
- * Inspired by the original jQuery.tipsy by Jason Frame
- * ===========================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ========================================================== */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* TOOLTIP PUBLIC CLASS DEFINITION
- * =============================== */
-
- var Tooltip = function (element, options) {
- this.init('tooltip', element, options)
- }
-
- Tooltip.prototype = {
-
- constructor: Tooltip
-
- , init: function (type, element, options) {
- var eventIn
- , eventOut
- , triggers
- , trigger
- , i
-
- this.type = type
- this.$element = $(element)
- this.options = this.getOptions(options)
- this.enabled = true
-
- triggers = this.options.trigger.split(' ')
-
- for (i = triggers.length; i--;) {
- trigger = triggers[i]
- if (trigger == 'click') {
- this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
- } else if (trigger != 'manual') {
- eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
- eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
- this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
- this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
- }
- }
-
- this.options.selector ?
- (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
- this.fixTitle()
- }
-
- , getOptions: function (options) {
- options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options)
-
- if (options.delay && typeof options.delay == 'number') {
- options.delay = {
- show: options.delay
- , hide: options.delay
- }
- }
-
- return options
- }
-
- , enter: function (e) {
- var defaults = $.fn[this.type].defaults
- , options = {}
- , self
-
- this._options && $.each(this._options, function (key, value) {
- if (defaults[key] != value) options[key] = value
- }, this)
-
- self = $(e.currentTarget)[this.type](options).data(this.type)
-
- if (!self.options.delay || !self.options.delay.show) return self.show()
-
- clearTimeout(this.timeout)
- self.hoverState = 'in'
- this.timeout = setTimeout(function() {
- if (self.hoverState == 'in') self.show()
- }, self.options.delay.show)
- }
-
- , leave: function (e) {
- var self = $(e.currentTarget)[this.type](this._options).data(this.type)
-
- if (this.timeout) clearTimeout(this.timeout)
- if (!self.options.delay || !self.options.delay.hide) return self.hide()
-
- self.hoverState = 'out'
- this.timeout = setTimeout(function() {
- if (self.hoverState == 'out') self.hide()
- }, self.options.delay.hide)
- }
-
- , show: function () {
- var $tip
- , pos
- , actualWidth
- , actualHeight
- , placement
- , tp
- , e = $.Event('show')
-
- if (this.hasContent() && this.enabled) {
- this.$element.trigger(e)
- if (e.isDefaultPrevented()) return
- $tip = this.tip()
- this.setContent()
-
- if (this.options.animation) {
- $tip.addClass('fade')
- }
-
- placement = typeof this.options.placement == 'function' ?
- this.options.placement.call(this, $tip[0], this.$element[0]) :
- this.options.placement
-
- $tip
- .detach()
- .css({ top: 0, left: 0, display: 'block' })
-
- this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
-
- pos = this.getPosition()
-
- actualWidth = $tip[0].offsetWidth
- actualHeight = $tip[0].offsetHeight
-
- switch (placement) {
- case 'bottom':
- tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
- break
- case 'top':
- tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
- break
- case 'left':
- tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
- break
- case 'right':
- tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
- break
- }
-
- this.applyPlacement(tp, placement)
- this.$element.trigger('shown')
- }
- }
-
- , applyPlacement: function(offset, placement){
- var $tip = this.tip()
- , width = $tip[0].offsetWidth
- , height = $tip[0].offsetHeight
- , actualWidth
- , actualHeight
- , delta
- , replace
-
- $tip
- .offset(offset)
- .addClass(placement)
- .addClass('in')
-
- actualWidth = $tip[0].offsetWidth
- actualHeight = $tip[0].offsetHeight
-
- if (placement == 'top' && actualHeight != height) {
- offset.top = offset.top + height - actualHeight
- replace = true
- }
-
- if (placement == 'bottom' || placement == 'top') {
- delta = 0
-
- if (offset.left < 0){
- delta = offset.left * -2
- offset.left = 0
- $tip.offset(offset)
- actualWidth = $tip[0].offsetWidth
- actualHeight = $tip[0].offsetHeight
- }
-
- this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
- } else {
- this.replaceArrow(actualHeight - height, actualHeight, 'top')
- }
-
- if (replace) $tip.offset(offset)
- }
-
- , replaceArrow: function(delta, dimension, position){
- this
- .arrow()
- .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
- }
-
- , setContent: function () {
- var $tip = this.tip()
- , title = this.getTitle()
-
- $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
- $tip.removeClass('fade in top bottom left right')
- }
-
- , hide: function () {
- var that = this
- , $tip = this.tip()
- , e = $.Event('hide')
-
- this.$element.trigger(e)
- if (e.isDefaultPrevented()) return
-
- $tip.removeClass('in')
-
- function removeWithAnimation() {
- var timeout = setTimeout(function () {
- $tip.off($.support.transition.end).detach()
- }, 500)
-
- $tip.one($.support.transition.end, function () {
- clearTimeout(timeout)
- $tip.detach()
- })
- }
-
- $.support.transition && this.$tip.hasClass('fade') ?
- removeWithAnimation() :
- $tip.detach()
-
- this.$element.trigger('hidden')
-
- return this
- }
-
- , fixTitle: function () {
- var $e = this.$element
- if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
- $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
- }
- }
-
- , hasContent: function () {
- return this.getTitle()
- }
-
- , getPosition: function () {
- var el = this.$element[0]
- return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
- width: el.offsetWidth
- , height: el.offsetHeight
- }, this.$element.offset())
- }
-
- , getTitle: function () {
- var title
- , $e = this.$element
- , o = this.options
-
- title = $e.attr('data-original-title')
- || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
-
- return title
- }
-
- , tip: function () {
- return this.$tip = this.$tip || $(this.options.template)
- }
-
- , arrow: function(){
- return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
- }
-
- , validate: function () {
- if (!this.$element[0].parentNode) {
- this.hide()
- this.$element = null
- this.options = null
- }
- }
-
- , enable: function () {
- this.enabled = true
- }
-
- , disable: function () {
- this.enabled = false
- }
-
- , toggleEnabled: function () {
- this.enabled = !this.enabled
- }
-
- , toggle: function (e) {
- var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
- self.tip().hasClass('in') ? self.hide() : self.show()
- }
-
- , destroy: function () {
- this.hide().$element.off('.' + this.type).removeData(this.type)
- }
-
- }
-
-
- /* TOOLTIP PLUGIN DEFINITION
- * ========================= */
-
- var old = $.fn.tooltip
-
- $.fn.tooltip = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('tooltip')
- , options = typeof option == 'object' && option
- if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.tooltip.Constructor = Tooltip
-
- $.fn.tooltip.defaults = {
- animation: true
- , placement: 'top'
- , selector: false
- , template: ''
- , trigger: 'hover focus'
- , title: ''
- , delay: 0
- , html: false
- , container: false
- }
-
-
- /* TOOLTIP NO CONFLICT
- * =================== */
-
- $.fn.tooltip.noConflict = function () {
- $.fn.tooltip = old
- return this
- }
-
-}(window.jQuery);
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-transition.js b/framework/yii/bootstrap/assets/js/bootstrap-transition.js
deleted file mode 100644
index e12cf6e..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-transition.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* ===================================================
- * bootstrap-transition.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#transitions
- * ===================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ========================================================== */
-
-
-!function ($) {
-
- "use strict"; // jshint ;_;
-
-
- /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
- * ======================================================= */
-
- $(function () {
-
- $.support.transition = (function () {
-
- var transitionEnd = (function () {
-
- var el = document.createElement('bootstrap')
- , transEndEventNames = {
- 'WebkitTransition' : 'webkitTransitionEnd'
- , 'MozTransition' : 'transitionend'
- , 'OTransition' : 'oTransitionEnd otransitionend'
- , 'transition' : 'transitionend'
- }
- , name
-
- for (name in transEndEventNames){
- if (el.style[name] !== undefined) {
- return transEndEventNames[name]
- }
- }
-
- }())
-
- return transitionEnd && {
- end: transitionEnd
- }
-
- })()
-
- })
-
-}(window.jQuery);
\ No newline at end of file
diff --git a/framework/yii/bootstrap/assets/js/bootstrap-typeahead.js b/framework/yii/bootstrap/assets/js/bootstrap-typeahead.js
deleted file mode 100644
index abc48d5..0000000
--- a/framework/yii/bootstrap/assets/js/bootstrap-typeahead.js
+++ /dev/null
@@ -1,335 +0,0 @@
-/* =============================================================
- * bootstrap-typeahead.js v2.3.2
- * http://twitter.github.com/bootstrap/javascript.html#typeahead
- * =============================================================
- * Copyright 2012 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============================================================ */
-
-
-!function($){
-
- "use strict"; // jshint ;_;
-
-
- /* TYPEAHEAD PUBLIC CLASS DEFINITION
- * ================================= */
-
- var Typeahead = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, $.fn.typeahead.defaults, options)
- this.matcher = this.options.matcher || this.matcher
- this.sorter = this.options.sorter || this.sorter
- this.highlighter = this.options.highlighter || this.highlighter
- this.updater = this.options.updater || this.updater
- this.source = this.options.source
- this.$menu = $(this.options.menu)
- this.shown = false
- this.listen()
- }
-
- Typeahead.prototype = {
-
- constructor: Typeahead
-
- , select: function () {
- var val = this.$menu.find('.active').attr('data-value')
- this.$element
- .val(this.updater(val))
- .change()
- return this.hide()
- }
-
- , updater: function (item) {
- return item
- }
-
- , show: function () {
- var pos = $.extend({}, this.$element.position(), {
- height: this.$element[0].offsetHeight
- })
-
- this.$menu
- .insertAfter(this.$element)
- .css({
- top: pos.top + pos.height
- , left: pos.left
- })
- .show()
-
- this.shown = true
- return this
- }
-
- , hide: function () {
- this.$menu.hide()
- this.shown = false
- return this
- }
-
- , lookup: function (event) {
- var items
-
- this.query = this.$element.val()
-
- if (!this.query || this.query.length < this.options.minLength) {
- return this.shown ? this.hide() : this
- }
-
- items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
-
- return items ? this.process(items) : this
- }
-
- , process: function (items) {
- var that = this
-
- items = $.grep(items, function (item) {
- return that.matcher(item)
- })
-
- items = this.sorter(items)
-
- if (!items.length) {
- return this.shown ? this.hide() : this
- }
-
- return this.render(items.slice(0, this.options.items)).show()
- }
-
- , matcher: function (item) {
- return ~item.toLowerCase().indexOf(this.query.toLowerCase())
- }
-
- , sorter: function (items) {
- var beginswith = []
- , caseSensitive = []
- , caseInsensitive = []
- , item
-
- while (item = items.shift()) {
- if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
- else if (~item.indexOf(this.query)) caseSensitive.push(item)
- else caseInsensitive.push(item)
- }
-
- return beginswith.concat(caseSensitive, caseInsensitive)
- }
-
- , highlighter: function (item) {
- var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
- return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
- return '' + match + ' '
- })
- }
-
- , render: function (items) {
- var that = this
-
- items = $(items).map(function (i, item) {
- i = $(that.options.item).attr('data-value', item)
- i.find('a').html(that.highlighter(item))
- return i[0]
- })
-
- items.first().addClass('active')
- this.$menu.html(items)
- return this
- }
-
- , next: function (event) {
- var active = this.$menu.find('.active').removeClass('active')
- , next = active.next()
-
- if (!next.length) {
- next = $(this.$menu.find('li')[0])
- }
-
- next.addClass('active')
- }
-
- , prev: function (event) {
- var active = this.$menu.find('.active').removeClass('active')
- , prev = active.prev()
-
- if (!prev.length) {
- prev = this.$menu.find('li').last()
- }
-
- prev.addClass('active')
- }
-
- , listen: function () {
- this.$element
- .on('focus', $.proxy(this.focus, this))
- .on('blur', $.proxy(this.blur, this))
- .on('keypress', $.proxy(this.keypress, this))
- .on('keyup', $.proxy(this.keyup, this))
-
- if (this.eventSupported('keydown')) {
- this.$element.on('keydown', $.proxy(this.keydown, this))
- }
-
- this.$menu
- .on('click', $.proxy(this.click, this))
- .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
- .on('mouseleave', 'li', $.proxy(this.mouseleave, this))
- }
-
- , eventSupported: function(eventName) {
- var isSupported = eventName in this.$element
- if (!isSupported) {
- this.$element.setAttribute(eventName, 'return;')
- isSupported = typeof this.$element[eventName] === 'function'
- }
- return isSupported
- }
-
- , move: function (e) {
- if (!this.shown) return
-
- switch(e.keyCode) {
- case 9: // tab
- case 13: // enter
- case 27: // escape
- e.preventDefault()
- break
-
- case 38: // up arrow
- e.preventDefault()
- this.prev()
- break
-
- case 40: // down arrow
- e.preventDefault()
- this.next()
- break
- }
-
- e.stopPropagation()
- }
-
- , keydown: function (e) {
- this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
- this.move(e)
- }
-
- , keypress: function (e) {
- if (this.suppressKeyPressRepeat) return
- this.move(e)
- }
-
- , keyup: function (e) {
- switch(e.keyCode) {
- case 40: // down arrow
- case 38: // up arrow
- case 16: // shift
- case 17: // ctrl
- case 18: // alt
- break
-
- case 9: // tab
- case 13: // enter
- if (!this.shown) return
- this.select()
- break
-
- case 27: // escape
- if (!this.shown) return
- this.hide()
- break
-
- default:
- this.lookup()
- }
-
- e.stopPropagation()
- e.preventDefault()
- }
-
- , focus: function (e) {
- this.focused = true
- }
-
- , blur: function (e) {
- this.focused = false
- if (!this.mousedover && this.shown) this.hide()
- }
-
- , click: function (e) {
- e.stopPropagation()
- e.preventDefault()
- this.select()
- this.$element.focus()
- }
-
- , mouseenter: function (e) {
- this.mousedover = true
- this.$menu.find('.active').removeClass('active')
- $(e.currentTarget).addClass('active')
- }
-
- , mouseleave: function (e) {
- this.mousedover = false
- if (!this.focused && this.shown) this.hide()
- }
-
- }
-
-
- /* TYPEAHEAD PLUGIN DEFINITION
- * =========================== */
-
- var old = $.fn.typeahead
-
- $.fn.typeahead = function (option) {
- return this.each(function () {
- var $this = $(this)
- , data = $this.data('typeahead')
- , options = typeof option == 'object' && option
- if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.typeahead.defaults = {
- source: []
- , items: 8
- , menu: ''
- , item: ' '
- , minLength: 1
- }
-
- $.fn.typeahead.Constructor = Typeahead
-
-
- /* TYPEAHEAD NO CONFLICT
- * =================== */
-
- $.fn.typeahead.noConflict = function () {
- $.fn.typeahead = old
- return this
- }
-
-
- /* TYPEAHEAD DATA-API
- * ================== */
-
- $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
- var $this = $(this)
- if ($this.data('typeahead')) return
- $this.typeahead($this.data())
- })
-
-}(window.jQuery);
diff --git a/framework/yii/caching/ApcCache.php b/framework/yii/caching/ApcCache.php
index ff3acf5..8a0d207 100644
--- a/framework/yii/caching/ApcCache.php
+++ b/framework/yii/caching/ApcCache.php
@@ -21,6 +21,22 @@ namespace yii\caching;
class ApcCache extends Cache
{
/**
+ * Checks whether a specified key exists in the cache.
+ * This can be faster than getting the value from the cache if the data is big.
+ * Note that this method does not check whether the dependency associated
+ * with the cached data, if there is any, has changed. So a call to [[get]]
+ * may return false while exists returns true.
+ * @param mixed $key a key identifying the cached value. This can be a simple string or
+ * a complex data structure consisting of factors representing the key.
+ * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+ */
+ public function exists($key)
+ {
+ $key = $this->buildKey($key);
+ return apc_exists($key);
+ }
+
+ /**
* Retrieves a value from cache with a specified key.
* This is the implementation of the method declared in the parent class.
* @param string $key a unique key identifying the cached value
@@ -56,6 +72,17 @@ class ApcCache extends Cache
}
/**
+ * Stores multiple key-value pairs in cache.
+ * @param array $data array where key corresponds to cache key while value
+ * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+ * @return array array of failed keys
+ */
+ protected function setValues($data, $expire)
+ {
+ return array_keys(apc_store($data, null, $expire));
+ }
+
+ /**
* Stores a value identified by a key into cache if the cache does not contain this key.
* This is the implementation of the method declared in the parent class.
* @param string $key the key identifying the value to be cached
@@ -69,6 +96,17 @@ class ApcCache extends Cache
}
/**
+ * Adds multiple key-value pairs to cache.
+ * @param array $data array where key corresponds to cache key while value is the value stored
+ * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+ * @return array array of failed keys
+ */
+ protected function addValues($data, $expire)
+ {
+ return array_keys(apc_add($data, null, $expire));
+ }
+
+ /**
* Deletes a value with the specified key from cache
* This is the implementation of the method declared in the parent class.
* @param string $key the key of the value to be deleted
@@ -86,6 +124,10 @@ class ApcCache extends Cache
*/
protected function flushValues()
{
- return apc_clear_cache('user');
+ if (extension_loaded('apcu')) {
+ return apc_clear_cache();
+ } else {
+ return apc_clear_cache('user');
+ }
}
}
diff --git a/framework/yii/caching/Cache.php b/framework/yii/caching/Cache.php
index fc1027c..31d1e2d 100644
--- a/framework/yii/caching/Cache.php
+++ b/framework/yii/caching/Cache.php
@@ -47,7 +47,6 @@ use yii\helpers\StringHelper;
* - [[deleteValue()]]: delete the value with the specified key from cache
* - [[flushValues()]]: delete all values from cache
*
- *
* @author Qiang Xue
* @since 2.0
*/
@@ -59,7 +58,7 @@ abstract class Cache extends Component implements \ArrayAccess
* if you don't want to use key prefix. It is recommended that you explicitly set this property to some
* static value if the cached data needs to be shared among multiple applications.
*
- * To ensure interoperability, only use alphanumeric characters should be used.
+ * To ensure interoperability, only alphanumeric characters should be used.
*/
public $keyPrefix;
/**
@@ -94,20 +93,14 @@ abstract class Cache extends Component implements \ArrayAccess
* If the given key is a string containing alphanumeric characters only and no more than 32 characters,
* then the key will be returned back prefixed with [[keyPrefix]]. Otherwise, a normalized key
* is generated by serializing the given key, applying MD5 hashing, and prefixing with [[keyPrefix]].
- *
- * The following example builds a cache key using three parameters:
- *
- * ~~~
- * $key = $cache->buildKey(array($className, $method, $id));
- * ~~~
*
* @param mixed $key the key to be normalized
* @return string the generated cache key
*/
- public function buildKey($key)
+ protected function buildKey($key)
{
if (is_string($key)) {
- $key = ctype_alnum($key) && StringHelper::strlen($key) <= 32 ? $key : md5($key);
+ $key = ctype_alnum($key) && StringHelper::byteLength($key) <= 32 ? $key : md5($key);
} else {
$key = md5(json_encode($key));
}
@@ -116,7 +109,8 @@ abstract class Cache extends Component implements \ArrayAccess
/**
* Retrieves a value from cache with a specified key.
- * @param string $key a key identifying the cached value
+ * @param mixed $key a key identifying the cached value. This can be a simple string or
+ * a complex data structure consisting of factors representing the key.
* @return mixed the value stored in cache, false if the value is not in the cache, expired,
* or the dependency associated with the cached data has changed.
*/
@@ -131,7 +125,7 @@ abstract class Cache extends Component implements \ArrayAccess
} else {
$value = call_user_func($this->serializer[1], $value);
}
- if (is_array($value) && !($value[1] instanceof Dependency && $value[1]->getHasChanged())) {
+ if (is_array($value) && !($value[1] instanceof Dependency && $value[1]->getHasChanged($this))) {
return $value[0];
} else {
return false;
@@ -139,6 +133,25 @@ abstract class Cache extends Component implements \ArrayAccess
}
/**
+ * Checks whether a specified key exists in the cache.
+ * This can be faster than getting the value from the cache if the data is big.
+ * In case a cache does not support this feature natively, this method will try to simulate it
+ * but has no performance improvement over getting it.
+ * Note that this method does not check whether the dependency associated
+ * with the cached data, if there is any, has changed. So a call to [[get]]
+ * may return false while exists returns true.
+ * @param mixed $key a key identifying the cached value. This can be a simple string or
+ * a complex data structure consisting of factors representing the key.
+ * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+ */
+ public function exists($key)
+ {
+ $key = $this->buildKey($key);
+ $value = $this->getValue($key);
+ return $value !== false;
+ }
+
+ /**
* Retrieves multiple values from cache with the specified keys.
* Some caches (such as memcache, apc) allow retrieving multiple cached values at the same time,
* which may improve the performance. In case a cache does not support this feature natively,
@@ -150,12 +163,12 @@ abstract class Cache extends Component implements \ArrayAccess
*/
public function mget($keys)
{
- $keyMap = array();
+ $keyMap = [];
foreach ($keys as $key) {
$keyMap[$key] = $this->buildKey($key);
}
$values = $this->getValues(array_values($keyMap));
- $results = array();
+ $results = [];
foreach ($keyMap as $key => $newKey) {
$results[$key] = false;
if (isset($values[$newKey])) {
@@ -165,7 +178,7 @@ abstract class Cache extends Component implements \ArrayAccess
$value = $this->serializer === null ? unserialize($values[$newKey])
: call_user_func($this->serializer[1], $values[$newKey]);
- if (is_array($value) && !($value[1] instanceof Dependency && $value[1]->getHasChanged())) {
+ if (is_array($value) && !($value[1] instanceof Dependency && $value[1]->getHasChanged($this))) {
$results[$key] = $value[0];
}
}
@@ -179,7 +192,8 @@ abstract class Cache extends Component implements \ArrayAccess
* If the cache already contains such a key, the existing value and
* expiration time will be replaced with the new ones, respectively.
*
- * @param string $key the key identifying the value to be cached
+ * @param mixed $key a key identifying the value to be cached. This can be a simple string or
+ * a complex data structure consisting of factors representing the key.
* @param mixed $value the value to be cached
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
* @param Dependency $dependency dependency of the cached item. If the dependency changes,
@@ -190,21 +204,85 @@ abstract class Cache extends Component implements \ArrayAccess
public function set($key, $value, $expire = 0, $dependency = null)
{
if ($dependency !== null && $this->serializer !== false) {
- $dependency->evaluateDependency();
+ $dependency->evaluateDependency($this);
}
if ($this->serializer === null) {
- $value = serialize(array($value, $dependency));
+ $value = serialize([$value, $dependency]);
} elseif ($this->serializer !== false) {
- $value = call_user_func($this->serializer[0], array($value, $dependency));
+ $value = call_user_func($this->serializer[0], [$value, $dependency]);
}
$key = $this->buildKey($key);
return $this->setValue($key, $value, $expire);
}
/**
+ * Stores multiple items in cache. Each item contains a value identified by a key.
+ * If the cache already contains such a key, the existing value and
+ * expiration time will be replaced with the new ones, respectively.
+ *
+ * @param array $items the items to be cached, as key-value pairs.
+ * @param integer $expire default number of seconds in which the cached values will expire. 0 means never expire.
+ * @param Dependency $dependency dependency of the cached items. If the dependency changes,
+ * the corresponding values in the cache will be invalidated when it is fetched via [[get()]].
+ * This parameter is ignored if [[serializer]] is false.
+ * @return boolean whether the items are successfully stored into cache
+ */
+ public function mset($items, $expire = 0, $dependency = null)
+ {
+ if ($dependency !== null && $this->serializer !== false) {
+ $dependency->evaluateDependency($this);
+ }
+
+ $data = [];
+ foreach ($items as $key => $value) {
+ $itemKey = $this->buildKey($key);
+ if ($this->serializer === null) {
+ $itemValue = serialize([$value, $dependency]);
+ } elseif ($this->serializer !== false) {
+ $itemValue = call_user_func($this->serializer[0], [$value, $dependency]);
+ }
+
+ $data[$itemKey] = $itemValue;
+ }
+ return $this->setValues($data, $expire);
+ }
+
+ /**
+ * Stores multiple items in cache. Each item contains a value identified by a key.
+ * If the cache already contains such a key, the existing value and expiration time will be preserved.
+ *
+ * @param array $items the items to be cached, as key-value pairs.
+ * @param integer $expire default number of seconds in which the cached values will expire. 0 means never expire.
+ * @param Dependency $dependency dependency of the cached items. If the dependency changes,
+ * the corresponding values in the cache will be invalidated when it is fetched via [[get()]].
+ * This parameter is ignored if [[serializer]] is false.
+ * @return boolean whether the items are successfully stored into cache
+ */
+ public function madd($items, $expire = 0, $dependency = null)
+ {
+ if ($dependency !== null && $this->serializer !== false) {
+ $dependency->evaluateDependency($this);
+ }
+
+ $data = [];
+ foreach ($items as $key => $value) {
+ $itemKey = $this->buildKey($key);
+ if ($this->serializer === null) {
+ $itemValue = serialize([$value, $dependency]);
+ } elseif ($this->serializer !== false) {
+ $itemValue = call_user_func($this->serializer[0], [$value, $dependency]);
+ }
+
+ $data[$itemKey] = $itemValue;
+ }
+ return $this->addValues($data, $expire);
+ }
+
+ /**
* Stores a value identified by a key into cache if the cache does not contain this key.
* Nothing will be done if the cache already contains the key.
- * @param string $key the key identifying the value to be cached
+ * @param mixed $key a key identifying the value to be cached. This can be a simple string or
+ * a complex data structure consisting of factors representing the key.
* @param mixed $value the value to be cached
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
* @param Dependency $dependency dependency of the cached item. If the dependency changes,
@@ -215,12 +293,12 @@ abstract class Cache extends Component implements \ArrayAccess
public function add($key, $value, $expire = 0, $dependency = null)
{
if ($dependency !== null && $this->serializer !== false) {
- $dependency->evaluateDependency();
+ $dependency->evaluateDependency($this);
}
if ($this->serializer === null) {
- $value = serialize(array($value, $dependency));
+ $value = serialize([$value, $dependency]);
} elseif ($this->serializer !== false) {
- $value = call_user_func($this->serializer[0], array($value, $dependency));
+ $value = call_user_func($this->serializer[0], [$value, $dependency]);
}
$key = $this->buildKey($key);
return $this->addValue($key, $value, $expire);
@@ -228,7 +306,8 @@ abstract class Cache extends Component implements \ArrayAccess
/**
* Deletes a value with the specified key from cache
- * @param string $key the key of the value to be deleted
+ * @param mixed $key a key identifying the value to be deleted from cache. This can be a simple string or
+ * a complex data structure consisting of factors representing the key.
* @return boolean if no error happens during deletion
*/
public function delete($key)
@@ -303,7 +382,7 @@ abstract class Cache extends Component implements \ArrayAccess
*/
protected function getValues($keys)
{
- $results = array();
+ $results = [];
foreach ($keys as $key) {
$results[$key] = $this->getValue($key);
}
@@ -311,6 +390,46 @@ abstract class Cache extends Component implements \ArrayAccess
}
/**
+ * Stores multiple key-value pairs in cache.
+ * The default implementation calls [[setValue()]] multiple times store values one by one. If the underlying cache
+ * storage supports multiset, this method should be overridden to exploit that feature.
+ * @param array $data array where key corresponds to cache key while value is the value stored
+ * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+ * @return array array of failed keys
+ */
+ protected function setValues($data, $expire)
+ {
+ $failedKeys = [];
+ foreach ($data as $key => $value)
+ {
+ if ($this->setValue($key, $value, $expire) === false) {
+ $failedKeys[] = $key;
+ }
+ }
+ return $failedKeys;
+ }
+
+ /**
+ * Adds multiple key-value pairs to cache.
+ * The default implementation calls [[addValue()]] multiple times add values one by one. If the underlying cache
+ * storage supports multiadd, this method should be overridden to exploit that feature.
+ * @param array $data array where key corresponds to cache key while value is the value stored
+ * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+ * @return array array of failed keys
+ */
+ protected function addValues($data, $expire)
+ {
+ $failedKeys = [];
+ foreach ($data as $key => $value)
+ {
+ if ($this->addValue($key, $value, $expire) === false) {
+ $failedKeys[] = $key;
+ }
+ }
+ return $failedKeys;
+ }
+
+ /**
* Returns whether there is a cache entry with a specified key.
* This method is required by the interface ArrayAccess.
* @param string $key a key identifying the cached value
diff --git a/framework/yii/caching/ChainedDependency.php b/framework/yii/caching/ChainedDependency.php
index 7c7058e..f8bfee3 100644
--- a/framework/yii/caching/ChainedDependency.php
+++ b/framework/yii/caching/ChainedDependency.php
@@ -14,8 +14,6 @@ namespace yii\caching;
* considered changed; When [[dependOnAll]] is false, if one of the dependencies has NOT changed,
* this dependency is considered NOT changed.
*
- * @property boolean $hasChanged Whether the dependency is changed or not.
- *
* @author Qiang Xue
* @since 2.0
*/
@@ -25,7 +23,7 @@ class ChainedDependency extends Dependency
* @var Dependency[] list of dependencies that this dependency is composed of.
* Each array element must be a dependency object.
*/
- public $dependencies;
+ public $dependencies = [];
/**
* @var boolean whether this dependency is depending on every dependency in [[dependencies]].
* Defaults to true, meaning if any of the dependencies has changed, this dependency is considered changed.
@@ -35,33 +33,23 @@ class ChainedDependency extends Dependency
public $dependOnAll = true;
/**
- * Constructor.
- * @param Dependency[] $dependencies list of dependencies that this dependency is composed of.
- * Each array element should be a dependency object.
- * @param array $config name-value pairs that will be used to initialize the object properties
- */
- public function __construct($dependencies = array(), $config = array())
- {
- $this->dependencies = $dependencies;
- parent::__construct($config);
- }
-
- /**
* Evaluates the dependency by generating and saving the data related with dependency.
+ * @param Cache $cache the cache component that is currently evaluating this dependency
*/
- public function evaluateDependency()
+ public function evaluateDependency($cache)
{
foreach ($this->dependencies as $dependency) {
- $dependency->evaluateDependency();
+ $dependency->evaluateDependency($cache);
}
}
/**
* Generates the data needed to determine if dependency has been changed.
* This method does nothing in this class.
+ * @param Cache $cache the cache component that is currently evaluating this dependency
* @return mixed the data needed to determine if dependency has been changed.
*/
- protected function generateDependencyData()
+ protected function generateDependencyData($cache)
{
return null;
}
@@ -70,14 +58,15 @@ class ChainedDependency extends Dependency
* Performs the actual dependency checking.
* This method returns true if any of the dependency objects
* reports a dependency change.
+ * @param Cache $cache the cache component that is currently evaluating this dependency
* @return boolean whether the dependency is changed or not.
*/
- public function getHasChanged()
+ public function getHasChanged($cache)
{
foreach ($this->dependencies as $dependency) {
- if ($this->dependOnAll && $dependency->getHasChanged()) {
+ if ($this->dependOnAll && $dependency->getHasChanged($cache)) {
return true;
- } elseif (!$this->dependOnAll && !$dependency->getHasChanged()) {
+ } elseif (!$this->dependOnAll && !$dependency->getHasChanged($cache)) {
return false;
}
}
diff --git a/framework/yii/caching/DbCache.php b/framework/yii/caching/DbCache.php
index 7571a42..cca2c55 100644
--- a/framework/yii/caching/DbCache.php
+++ b/framework/yii/caching/DbCache.php
@@ -23,11 +23,11 @@ use yii\db\Query;
* The following example shows how you can configure the application to use DbCache:
*
* ~~~
- * 'cache' => array(
+ * 'cache' => [
* 'class' => 'yii\caching\DbCache',
* // 'db' => 'mydb',
* // 'cacheTable' => 'my_cache',
- * )
+ * ]
* ~~~
*
* @author Qiang Xue
@@ -89,6 +89,35 @@ class DbCache extends Cache
}
/**
+ * Checks whether a specified key exists in the cache.
+ * This can be faster than getting the value from the cache if the data is big.
+ * Note that this method does not check whether the dependency associated
+ * with the cached data, if there is any, has changed. So a call to [[get]]
+ * may return false while exists returns true.
+ * @param mixed $key a key identifying the cached value. This can be a simple string or
+ * a complex data structure consisting of factors representing the key.
+ * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+ */
+ public function exists($key)
+ {
+ $key = $this->buildKey($key);
+
+ $query = new Query;
+ $query->select(['COUNT(*)'])
+ ->from($this->cacheTable)
+ ->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', [':id' => $key]);
+ if ($this->db->enableQueryCache) {
+ // temporarily disable and re-enable query caching
+ $this->db->enableQueryCache = false;
+ $result = $query->createCommand($this->db)->queryScalar();
+ $this->db->enableQueryCache = true;
+ } else {
+ $result = $query->createCommand($this->db)->queryScalar();
+ }
+ return $result > 0;
+ }
+
+ /**
* Retrieves a value from cache with a specified key.
* This is the implementation of the method declared in the parent class.
* @param string $key a unique key identifying the cached value
@@ -97,9 +126,9 @@ class DbCache extends Cache
protected function getValue($key)
{
$query = new Query;
- $query->select(array('data'))
+ $query->select(['data'])
->from($this->cacheTable)
- ->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', array(':id' => $key));
+ ->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', [':id' => $key]);
if ($this->db->enableQueryCache) {
// temporarily disable and re-enable query caching
$this->db->enableQueryCache = false;
@@ -119,12 +148,12 @@ class DbCache extends Cache
protected function getValues($keys)
{
if (empty($keys)) {
- return array();
+ return [];
}
$query = new Query;
- $query->select(array('id', 'data'))
+ $query->select(['id', 'data'])
->from($this->cacheTable)
- ->where(array('id' => $keys))
+ ->where(['id' => $keys])
->andWhere('([[expire]] = 0 OR [[expire]] > ' . time() . ')');
if ($this->db->enableQueryCache) {
@@ -135,7 +164,7 @@ class DbCache extends Cache
$rows = $query->createCommand($this->db)->queryAll();
}
- $results = array();
+ $results = [];
foreach ($keys as $key) {
$results[$key] = false;
}
@@ -157,12 +186,10 @@ class DbCache extends Cache
protected function setValue($key, $value, $expire)
{
$command = $this->db->createCommand()
- ->update($this->cacheTable, array(
+ ->update($this->cacheTable, [
'expire' => $expire > 0 ? $expire + time() : 0,
- 'data' => array($value, \PDO::PARAM_LOB),
- ), array(
- 'id' => $key,
- ));
+ 'data' => [$value, \PDO::PARAM_LOB],
+ ], ['id' => $key]);
if ($command->execute()) {
$this->gc();
@@ -193,11 +220,11 @@ class DbCache extends Cache
try {
$this->db->createCommand()
- ->insert($this->cacheTable, array(
+ ->insert($this->cacheTable, [
'id' => $key,
'expire' => $expire,
- 'data' => array($value, \PDO::PARAM_LOB),
- ))->execute();
+ 'data' => [$value, \PDO::PARAM_LOB],
+ ])->execute();
return true;
} catch (\Exception $e) {
return false;
@@ -213,7 +240,7 @@ class DbCache extends Cache
protected function deleteValue($key)
{
$this->db->createCommand()
- ->delete($this->cacheTable, array('id' => $key))
+ ->delete($this->cacheTable, ['id' => $key])
->execute();
return true;
}
diff --git a/framework/yii/caching/DbDependency.php b/framework/yii/caching/DbDependency.php
index fd92aea..ac6f2e7 100644
--- a/framework/yii/caching/DbDependency.php
+++ b/framework/yii/caching/DbDependency.php
@@ -34,41 +34,32 @@ class DbDependency extends Dependency
/**
* @var array the parameters (name => value) to be bound to the SQL statement specified by [[sql]].
*/
- public $params;
-
- /**
- * Constructor.
- * @param string $sql the SQL query whose result is used to determine if the dependency has been changed.
- * @param array $params the parameters (name => value) to be bound to the SQL statement specified by [[sql]].
- * @param array $config name-value pairs that will be used to initialize the object properties
- */
- public function __construct($sql, $params = array(), $config = array())
- {
- $this->sql = $sql;
- $this->params = $params;
- parent::__construct($config);
- }
+ public $params = [];
/**
* Generates the data needed to determine if dependency has been changed.
* This method returns the value of the global state.
- * @throws InvalidConfigException
+ * @param Cache $cache the cache component that is currently evaluating this dependency
* @return mixed the data needed to determine if dependency has been changed.
+ * @throws InvalidConfigException if [[db]] is not a valid application component ID
*/
- protected function generateDependencyData()
+ protected function generateDependencyData($cache)
{
$db = Yii::$app->getComponent($this->db);
if (!$db instanceof Connection) {
throw new InvalidConfigException("DbDependency::db must be the application component ID of a DB connection.");
}
+ if ($this->sql === null) {
+ throw new InvalidConfigException("DbDependency::sql must be set.");
+ }
if ($db->enableQueryCache) {
// temporarily disable and re-enable query caching
$db->enableQueryCache = false;
- $result = $db->createCommand($this->sql, $this->params)->queryRow();
+ $result = $db->createCommand($this->sql, $this->params)->queryOne();
$db->enableQueryCache = true;
} else {
- $result = $db->createCommand($this->sql, $this->params)->queryRow();
+ $result = $db->createCommand($this->sql, $this->params)->queryOne();
}
return $result;
}
diff --git a/framework/yii/caching/Dependency.php b/framework/yii/caching/Dependency.php
index f0587f9..c84f72d 100644
--- a/framework/yii/caching/Dependency.php
+++ b/framework/yii/caching/Dependency.php
@@ -13,8 +13,6 @@ namespace yii\caching;
* Child classes should override its [[generateDependencyData()]] for generating
* the actual dependency data.
*
- * @property boolean $hasChanged Whether the dependency has changed.
- *
* @author Qiang Xue
* @since 2.0
*/
@@ -36,7 +34,7 @@ abstract class Dependency extends \yii\base\Object
/**
* @var array static storage of cached data for reusable dependencies.
*/
- private static $_reusableData = array();
+ private static $_reusableData = [];
/**
* @var string a unique hash value for this cache dependency.
*/
@@ -46,37 +44,40 @@ abstract class Dependency extends \yii\base\Object
/**
* Evaluates the dependency by generating and saving the data related with dependency.
* This method is invoked by cache before writing data into it.
+ * @param Cache $cache the cache component that is currently evaluating this dependency
*/
- public function evaluateDependency()
+ public function evaluateDependency($cache)
{
if (!$this->reusable) {
- $this->data = $this->generateDependencyData();
+ $this->data = $this->generateDependencyData($cache);
} else {
if ($this->_hash === null) {
$this->_hash = sha1(serialize($this));
}
if (!array_key_exists($this->_hash, self::$_reusableData)) {
- self::$_reusableData[$this->_hash] = $this->generateDependencyData();
+ self::$_reusableData[$this->_hash] = $this->generateDependencyData($cache);
}
$this->data = self::$_reusableData[$this->_hash];
}
}
/**
+ * Returns a value indicating whether the dependency has changed.
+ * @param Cache $cache the cache component that is currently evaluating this dependency
* @return boolean whether the dependency has changed.
*/
- public function getHasChanged()
+ public function getHasChanged($cache)
{
if (!$this->reusable) {
- return $this->generateDependencyData() !== $this->data;
+ return $this->generateDependencyData($cache) !== $this->data;
} else {
if ($this->_hash === null) {
$this->_hash = sha1(serialize($this));
}
if (!array_key_exists($this->_hash, self::$_reusableData)) {
- self::$_reusableData[$this->_hash] = $this->generateDependencyData();
+ self::$_reusableData[$this->_hash] = $this->generateDependencyData($cache);
}
- return self::$_reusableData[$this->_hash] !== $this->_data;
+ return self::$_reusableData[$this->_hash] !== $this->data;
}
}
@@ -85,13 +86,14 @@ abstract class Dependency extends \yii\base\Object
*/
public static function resetReusableData()
{
- self::$_reusableData = array();
+ self::$_reusableData = [];
}
/**
* Generates the data needed to determine if dependency has been changed.
* Derived classes should override this method to generate the actual dependency data.
+ * @param Cache $cache the cache component that is currently evaluating this dependency
* @return mixed the data needed to determine if dependency has been changed.
*/
- abstract protected function generateDependencyData();
+ abstract protected function generateDependencyData($cache);
}
diff --git a/framework/yii/caching/ExpressionDependency.php b/framework/yii/caching/ExpressionDependency.php
index ec4736c..f6642b4 100644
--- a/framework/yii/caching/ExpressionDependency.php
+++ b/framework/yii/caching/ExpressionDependency.php
@@ -24,35 +24,23 @@ class ExpressionDependency extends Dependency
{
/**
* @var string the string representation of a PHP expression whose result is used to determine the dependency.
- * A PHP expression can be any PHP code that has a value. To learn more about what an expression is,
+ * A PHP expression can be any PHP code that evaluates to a value. To learn more about what an expression is,
* please refer to the [php manual](http://www.php.net/manual/en/language.expressions.php).
*/
- public $expression;
+ public $expression = 'true';
/**
- * @var mixed custom data associated with this dependency. In [[expression]], you may compare the value of
- * this property with the latest data to determine if the dependency has changed or not.
+ * @var mixed custom parameters associated with this dependency. You may get the value
+ * of this property in [[expression]] using `$this->params`.
*/
- public $data;
-
- /**
- * Constructor.
- * @param string $expression the PHP expression whose result is used to determine the dependency.
- * @param mixed $data the custom data associated with this dependency
- * @param array $config name-value pairs that will be used to initialize the object properties
- */
- public function __construct($expression = 'true', $data = null, $config = array())
- {
- $this->expression = $expression;
- $this->data = $data;
- parent::__construct($config);
- }
+ public $params;
/**
* Generates the data needed to determine if dependency has been changed.
* This method returns the result of the PHP expression.
+ * @param Cache $cache the cache component that is currently evaluating this dependency
* @return mixed the data needed to determine if dependency has been changed.
*/
- protected function generateDependencyData()
+ protected function generateDependencyData($cache)
{
return eval("return {$this->expression};");
}
diff --git a/framework/yii/caching/FileCache.php b/framework/yii/caching/FileCache.php
index a15751e..32cdf58 100644
--- a/framework/yii/caching/FileCache.php
+++ b/framework/yii/caching/FileCache.php
@@ -8,6 +8,7 @@
namespace yii\caching;
use Yii;
+use yii\helpers\FileHelper;
/**
* FileCache implements a cache component using files.
@@ -45,6 +46,20 @@ class FileCache extends Cache
* This number should be between 0 and 1000000. A value 0 means no GC will be performed at all.
**/
public $gcProbability = 10;
+ /**
+ * @var integer the permission to be set for newly created cache files.
+ * This value will be used by PHP chmod() function. No umask will be applied.
+ * If not set, the permission will be determined by the current environment.
+ */
+ public $fileMode;
+ /**
+ * @var integer the permission to be set for newly created directories.
+ * This value will be used by PHP chmod() function. No umask will be applied.
+ * Defaults to 0775, meaning the directory is read-writable by owner and group,
+ * but read-only for other users.
+ */
+ public $dirMode = 0775;
+
/**
* Initializes this component by ensuring the existence of the cache path.
@@ -54,11 +69,27 @@ class FileCache extends Cache
parent::init();
$this->cachePath = Yii::getAlias($this->cachePath);
if (!is_dir($this->cachePath)) {
- mkdir($this->cachePath, 0777, true);
+ FileHelper::createDirectory($this->cachePath, $this->dirMode, true);
}
}
/**
+ * Checks whether a specified key exists in the cache.
+ * This can be faster than getting the value from the cache if the data is big.
+ * Note that this method does not check whether the dependency associated
+ * with the cached data, if there is any, has changed. So a call to [[get]]
+ * may return false while exists returns true.
+ * @param mixed $key a key identifying the cached value. This can be a simple string or
+ * a complex data structure consisting of factors representing the key.
+ * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+ */
+ public function exists($key)
+ {
+ $cacheFile = $this->getCacheFile($this->buildKey($key));
+ return @filemtime($cacheFile) > time();
+ }
+
+ /**
* Retrieves a value from cache with a specified key.
* This is the implementation of the method declared in the parent class.
* @param string $key a unique key identifying the cached value
@@ -67,7 +98,7 @@ class FileCache extends Cache
protected function getValue($key)
{
$cacheFile = $this->getCacheFile($key);
- if (($time = @filemtime($cacheFile)) > time()) {
+ if (@filemtime($cacheFile) > time()) {
return @file_get_contents($cacheFile);
} else {
return false;
@@ -92,10 +123,12 @@ class FileCache extends Cache
$cacheFile = $this->getCacheFile($key);
if ($this->directoryLevel > 0) {
- @mkdir(dirname($cacheFile), 0777, true);
+ @FileHelper::createDirectory(dirname($cacheFile), $this->dirMode, true);
}
if (@file_put_contents($cacheFile, $value, LOCK_EX) !== false) {
- @chmod($cacheFile, 0777);
+ if ($this->fileMode !== null) {
+ @chmod($cacheFile, $this->fileMode);
+ }
return @touch($cacheFile, $expire);
} else {
return false;
diff --git a/framework/yii/caching/FileDependency.php b/framework/yii/caching/FileDependency.php
index 3797dde..11afde3 100644
--- a/framework/yii/caching/FileDependency.php
+++ b/framework/yii/caching/FileDependency.php
@@ -6,6 +6,7 @@
*/
namespace yii\caching;
+use yii\base\InvalidConfigException;
/**
* FileDependency represents a dependency based on a file's last modification time.
@@ -25,23 +26,17 @@ class FileDependency extends Dependency
public $fileName;
/**
- * Constructor.
- * @param string $fileName name of the file whose change is to be checked.
- * @param array $config name-value pairs that will be used to initialize the object properties
- */
- public function __construct($fileName = null, $config = array())
- {
- $this->fileName = $fileName;
- parent::__construct($config);
- }
-
- /**
* Generates the data needed to determine if dependency has been changed.
* This method returns the file's last modification time.
+ * @param Cache $cache the cache component that is currently evaluating this dependency
* @return mixed the data needed to determine if dependency has been changed.
+ * @throws InvalidConfigException if [[fileName]] is not set
*/
- protected function generateDependencyData()
+ protected function generateDependencyData($cache)
{
+ if ($this->fileName === null) {
+ throw new InvalidConfigException('FileDependency::fileName must be set');
+ }
return @filemtime($this->fileName);
}
}
diff --git a/framework/yii/caching/GroupDependency.php b/framework/yii/caching/GroupDependency.php
new file mode 100644
index 0000000..1cf7869
--- /dev/null
+++ b/framework/yii/caching/GroupDependency.php
@@ -0,0 +1,73 @@
+
+ * @since 2.0
+ */
+class GroupDependency extends Dependency
+{
+ /**
+ * @var string the group name. This property must be set.
+ */
+ public $group;
+
+ /**
+ * Generates the data needed to determine if dependency has been changed.
+ * This method does nothing in this class.
+ * @param Cache $cache the cache component that is currently evaluating this dependency
+ * @return mixed the data needed to determine if dependency has been changed.
+ * @throws InvalidConfigException if [[group]] is not set.
+ */
+ protected function generateDependencyData($cache)
+ {
+ if ($this->group === null) {
+ throw new InvalidConfigException('GroupDependency::group must be set');
+ }
+ $version = $cache->get([__CLASS__, $this->group]);
+ if ($version === false) {
+ $version = $this->invalidate($cache, $this->group);
+ }
+ return $version;
+ }
+
+ /**
+ * Performs the actual dependency checking.
+ * @param Cache $cache the cache component that is currently evaluating this dependency
+ * @return boolean whether the dependency is changed or not.
+ * @throws InvalidConfigException if [[group]] is not set.
+ */
+ public function getHasChanged($cache)
+ {
+ if ($this->group === null) {
+ throw new InvalidConfigException('GroupDependency::group must be set');
+ }
+ $version = $cache->get([__CLASS__, $this->group]);
+ return $version === false || $version !== $this->data;
+ }
+
+ /**
+ * Invalidates all of the cached data items that have the same [[group]].
+ * @param Cache $cache the cache component that caches the data items
+ * @param string $group the group name
+ * @return string the current version number
+ */
+ public static function invalidate($cache, $group)
+ {
+ $version = microtime();
+ $cache->set([__CLASS__, $group], $version);
+ return $version;
+ }
+}
diff --git a/framework/yii/caching/MemCache.php b/framework/yii/caching/MemCache.php
index 34c10d0..3391796 100644
--- a/framework/yii/caching/MemCache.php
+++ b/framework/yii/caching/MemCache.php
@@ -28,32 +28,34 @@ use yii\base\InvalidConfigException;
* To use MemCache as the cache application component, configure the application as follows,
*
* ~~~
- * array(
- * 'components' => array(
- * 'cache' => array(
- * 'class' => 'MemCache',
- * 'servers' => array(
- * array(
+ * [
+ * 'components' => [
+ * 'cache' => [
+ * 'class' => 'yii\caching\MemCache',
+ * 'servers' => [
+ * [
* 'host' => 'server1',
* 'port' => 11211,
* 'weight' => 60,
- * ),
- * array(
+ * ],
+ * [
* 'host' => 'server2',
* 'port' => 11211,
* 'weight' => 40,
- * ),
- * ),
- * ),
- * ),
- * )
+ * ],
+ * ],
+ * ],
+ * ],
+ * ]
* ~~~
*
* In the above, two memcache servers are used: server1 and server2. You can configure more properties of
* each server, such as `persistent`, `weight`, `timeout`. Please see [[MemCacheServer]] for available options.
*
- * @property \Memcache|\Memcached $memCache The memcache instance (or memcached if [[useMemcached]] is true) used by this component.
- * @property MemCacheServer[] $servers List of memcache server configurations.
+ * @property \Memcache|\Memcached $memcache The memcache (or memcached) object used by this cache component.
+ * This property is read-only.
+ * @property MemCacheServer[] $servers List of memcache server configurations. Note that the type of this
+ * property differs in getter and setter. See [[getServers()]] and [[setServers()]] for details.
*
* @author Qiang Xue
* @since 2.0
@@ -74,7 +76,7 @@ class MemCache extends Cache
/**
* @var array list of memcache server configurations
*/
- private $_servers = array();
+ private $_servers = [];
/**
* Initializes this application component.
@@ -85,7 +87,14 @@ class MemCache extends Cache
parent::init();
$servers = $this->getServers();
$cache = $this->getMemCache();
- if (count($servers)) {
+ if (empty($servers)) {
+ $cache->addServer('127.0.0.1', 11211);
+ } else {
+ if (!$this->useMemcached) {
+ // different version of memcache may have different number of parameters for the addServer method.
+ $class = new \ReflectionClass($cache);
+ $paramCount = $class->getMethod('addServer')->getNumberOfParameters();
+ }
foreach ($servers as $server) {
if ($server->host === null) {
throw new InvalidConfigException("The 'host' property must be specified for every memcache server.");
@@ -93,12 +102,23 @@ class MemCache extends Cache
if ($this->useMemcached) {
$cache->addServer($server->host, $server->port, $server->weight);
} else {
- $cache->addServer($server->host, $server->port, $server->persistent,
- $server->weight, $server->timeout, $server->retryInterval, $server->status);
+ // $timeout is used for memcache versions that do not have timeoutms parameter
+ $timeout = (int) ($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0);
+ if ($paramCount === 9) {
+ $cache->addServer(
+ $server->host, $server->port, $server->persistent,
+ $server->weight, $timeout, $server->retryInterval,
+ $server->status, $server->failureCallback, $server->timeout
+ );
+ } else {
+ $cache->addServer(
+ $server->host, $server->port, $server->persistent,
+ $server->weight, $timeout, $server->retryInterval,
+ $server->status, $server->failureCallback
+ );
+ }
}
}
- } else {
- $cache->addServer('127.0.0.1', 11211);
}
}
@@ -182,6 +202,27 @@ class MemCache extends Cache
}
/**
+ * Stores multiple key-value pairs in cache.
+ * @param array $data array where key corresponds to cache key while value is the value stored
+ * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+ * @return array array of failed keys. Always empty in case of using memcached.
+ */
+ protected function setValues($data, $expire)
+ {
+ if ($this->useMemcached) {
+ if ($expire > 0) {
+ $expire += time();
+ } else {
+ $expire = 0;
+ }
+ $this->_cache->setMulti($data, $expire);
+ return [];
+ } else {
+ return parent::setValues($data, $expire);
+ }
+ }
+
+ /**
* Stores a value identified by a key into cache if the cache does not contain this key.
* This is the implementation of the method declared in the parent class.
*
diff --git a/framework/yii/caching/MemCacheServer.php b/framework/yii/caching/MemCacheServer.php
index dc0de08..b1b7727 100644
--- a/framework/yii/caching/MemCacheServer.php
+++ b/framework/yii/caching/MemCacheServer.php
@@ -35,9 +35,11 @@ class MemCacheServer extends \yii\base\Object
*/
public $persistent = true;
/**
- * @var integer value in seconds which will be used for connecting to the server. This is used by memcache only.
+ * @var integer timeout in milliseconds which will be used for connecting to the server.
+ * This is used by memcache only. For old versions of memcache that only support specifying
+ * timeout in seconds this will be rounded up to full seconds.
*/
- public $timeout = 15;
+ public $timeout = 1000;
/**
* @var integer how often a failed server will be retried (in seconds). This is used by memcache only.
*/
@@ -46,4 +48,11 @@ class MemCacheServer extends \yii\base\Object
* @var boolean if the server should be flagged as online upon a failure. This is used by memcache only.
*/
public $status = true;
+ /**
+ * @var \Closure this callback function will run upon encountering an error.
+ * The callback is run before fail over is attempted. The function takes two parameters,
+ * the [[host]] and the [[port]] of the failed server.
+ * This is used by memcache only.
+ */
+ public $failureCallback;
}
diff --git a/framework/yii/caching/WinCache.php b/framework/yii/caching/WinCache.php
index eed580d..6b80f3c 100644
--- a/framework/yii/caching/WinCache.php
+++ b/framework/yii/caching/WinCache.php
@@ -8,12 +8,12 @@
namespace yii\caching;
/**
- * WinCache provides XCache caching in terms of an application component.
+ * WinCache provides Windows Cache caching in terms of an application component.
*
* To use this application component, the [WinCache PHP extension](http://www.iis.net/expand/wincacheforphp)
* must be loaded. Also note that "wincache.ucenabled" should be set to "On" in your php.ini file.
*
- * See {@link CCache} manual for common cache operations that are supported by WinCache.
+ * See [[Cache]] manual for common cache operations that are supported by WinCache.
*
* @author Qiang Xue
* @since 2.0
@@ -21,6 +21,22 @@ namespace yii\caching;
class WinCache extends Cache
{
/**
+ * Checks whether a specified key exists in the cache.
+ * This can be faster than getting the value from the cache if the data is big.
+ * Note that this method does not check whether the dependency associated
+ * with the cached data, if there is any, has changed. So a call to [[get]]
+ * may return false while exists returns true.
+ * @param mixed $key a key identifying the cached value. This can be a simple string or
+ * a complex data structure consisting of factors representing the key.
+ * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+ */
+ public function exists($key)
+ {
+ $key = $this->buildKey($key);
+ return wincache_ucache_exists($key);
+ }
+
+ /**
* Retrieves a value from cache with a specified key.
* This is the implementation of the method declared in the parent class.
* @param string $key a unique key identifying the cached value
@@ -56,6 +72,17 @@ class WinCache extends Cache
}
/**
+ * Stores multiple key-value pairs in cache.
+ * @param array $data array where key corresponds to cache key while value is the value stored
+ * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+ * @return array array of failed keys
+ */
+ protected function setValues($data, $expire)
+ {
+ return wincache_ucache_set($data, null, $expire);
+ }
+
+ /**
* Stores a value identified by a key into cache if the cache does not contain this key.
* This is the implementation of the method declared in the parent class.
*
@@ -70,6 +97,19 @@ class WinCache extends Cache
}
/**
+ * Adds multiple key-value pairs to cache.
+ * The default implementation calls [[addValue()]] multiple times add values one by one. If the underlying cache
+ * storage supports multiadd, this method should be overridden to exploit that feature.
+ * @param array $data array where key corresponds to cache key while value is the value stored
+ * @param integer $expire the number of seconds in which the cached values will expire. 0 means never expire.
+ * @return array array of failed keys
+ */
+ protected function addValues($data, $expire)
+ {
+ return wincache_ucache_add($data, null, $expire);
+ }
+
+ /**
* Deletes a value with the specified key from cache
* This is the implementation of the method declared in the parent class.
* @param string $key the key of the value to be deleted
diff --git a/framework/yii/caching/XCache.php b/framework/yii/caching/XCache.php
index 1f12f23..4221a39 100644
--- a/framework/yii/caching/XCache.php
+++ b/framework/yii/caching/XCache.php
@@ -22,6 +22,22 @@ namespace yii\caching;
class XCache extends Cache
{
/**
+ * Checks whether a specified key exists in the cache.
+ * This can be faster than getting the value from the cache if the data is big.
+ * Note that this method does not check whether the dependency associated
+ * with the cached data, if there is any, has changed. So a call to [[get]]
+ * may return false while exists returns true.
+ * @param mixed $key a key identifying the cached value. This can be a simple string or
+ * a complex data structure consisting of factors representing the key.
+ * @return boolean true if a value exists in cache, false if the value is not in the cache or expired.
+ */
+ public function exists($key)
+ {
+ $key = $this->buildKey($key);
+ return xcache_isset($key);
+ }
+
+ /**
* Retrieves a value from cache with a specified key.
* This is the implementation of the method declared in the parent class.
* @param string $key a unique key identifying the cached value
diff --git a/framework/yii/captcha/Captcha.php b/framework/yii/captcha/Captcha.php
new file mode 100644
index 0000000..18b8765
--- /dev/null
+++ b/framework/yii/captcha/Captcha.php
@@ -0,0 +1,134 @@
+
+ * @since 2.0
+ */
+class Captcha extends InputWidget
+{
+ /**
+ * @var string the route of the action that generates the CAPTCHA images.
+ * The action represented by this route must be an action of [[CaptchaAction]].
+ */
+ public $captchaAction = 'site/captcha';
+ /**
+ * @var array HTML attributes to be applied to the CAPTCHA image tag.
+ */
+ public $imageOptions = [];
+ /**
+ * @var string the template for arranging the CAPTCHA image tag and the text input tag.
+ * In this template, the token `{image}` will be replaced with the actual image tag,
+ * while `{input}` will be replaced with the text input tag.
+ */
+ public $template = '{image} {input}';
+
+ /**
+ * Initializes the widget.
+ */
+ public function init()
+ {
+ parent::init();
+
+ $this->checkRequirements();
+
+ if (!isset($this->imageOptions['id'])) {
+ $this->imageOptions['id'] = $this->options['id'] . '-image';
+ }
+ }
+
+ /**
+ * Renders the widget.
+ */
+ public function run()
+ {
+ $this->registerClientScript();
+ if ($this->hasModel()) {
+ $input = Html::activeTextInput($this->model, $this->attribute, $this->options);
+ } else {
+ $input = Html::textInput($this->name, $this->value, $this->options);
+ }
+ $url = Yii::$app->getUrlManager()->createUrl($this->captchaAction, ['v' => uniqid()]);
+ $image = Html::img($url, $this->imageOptions);
+ echo strtr($this->template, [
+ '{input}' => $input,
+ '{image}' => $image,
+ ]);
+ }
+
+ /**
+ * Registers the needed JavaScript.
+ */
+ public function registerClientScript()
+ {
+ $options = $this->getClientOptions();
+ $options = empty($options) ? '' : Json::encode($options);
+ $id = $this->imageOptions['id'];
+ $view = $this->getView();
+ CaptchaAsset::register($view);
+ $view->registerJs("jQuery('#$id').yiiCaptcha($options);");
+ }
+
+ /**
+ * Returns the options for the captcha JS widget.
+ * @return array the options
+ */
+ protected function getClientOptions()
+ {
+ $options = [
+ 'refreshUrl' => Html::url([$this->captchaAction, CaptchaAction::REFRESH_GET_VAR => 1]),
+ 'hashKey' => "yiiCaptcha/{$this->captchaAction}",
+ ];
+ return $options;
+ }
+
+ /**
+ * Checks if there is graphic extension available to generate CAPTCHA images.
+ * This method will check the existence of ImageMagick and GD extensions.
+ * @return string the name of the graphic extension, either "imagick" or "gd".
+ * @throws InvalidConfigException if neither ImageMagick nor GD is installed.
+ */
+ public static function checkRequirements()
+ {
+ if (extension_loaded('imagick')) {
+ $imagick = new \Imagick();
+ $imagickFormats = $imagick->queryFormats('PNG');
+ if (in_array('PNG', $imagickFormats)) {
+ return 'imagick';
+ }
+ }
+ if (extension_loaded('gd')) {
+ $gdInfo = gd_info();
+ if (!empty($gdInfo['FreeType Support'])) {
+ return 'gd';
+ }
+ }
+ throw new InvalidConfigException('GD with FreeType or ImageMagick PHP extensions are required.');
+ }
+}
diff --git a/framework/yii/captcha/CaptchaAction.php b/framework/yii/captcha/CaptchaAction.php
new file mode 100644
index 0000000..190ec35
--- /dev/null
+++ b/framework/yii/captcha/CaptchaAction.php
@@ -0,0 +1,341 @@
+
+ * @since 2.0
+ */
+class CaptchaAction extends Action
+{
+ /**
+ * The name of the GET parameter indicating whether the CAPTCHA image should be regenerated.
+ */
+ const REFRESH_GET_VAR = 'refresh';
+ /**
+ * @var integer how many times should the same CAPTCHA be displayed. Defaults to 3.
+ * A value less than or equal to 0 means the test is unlimited (available since version 1.1.2).
+ */
+ public $testLimit = 3;
+ /**
+ * @var integer the width of the generated CAPTCHA image. Defaults to 120.
+ */
+ public $width = 120;
+ /**
+ * @var integer the height of the generated CAPTCHA image. Defaults to 50.
+ */
+ public $height = 50;
+ /**
+ * @var integer padding around the text. Defaults to 2.
+ */
+ public $padding = 2;
+ /**
+ * @var integer the background color. For example, 0x55FF00.
+ * Defaults to 0xFFFFFF, meaning white color.
+ */
+ public $backColor = 0xFFFFFF;
+ /**
+ * @var integer the font color. For example, 0x55FF00. Defaults to 0x2040A0 (blue color).
+ */
+ public $foreColor = 0x2040A0;
+ /**
+ * @var boolean whether to use transparent background. Defaults to false.
+ */
+ public $transparent = false;
+ /**
+ * @var integer the minimum length for randomly generated word. Defaults to 6.
+ */
+ public $minLength = 6;
+ /**
+ * @var integer the maximum length for randomly generated word. Defaults to 7.
+ */
+ public $maxLength = 7;
+ /**
+ * @var integer the offset between characters. Defaults to -2. You can adjust this property
+ * in order to decrease or increase the readability of the captcha.
+ **/
+ public $offset = -2;
+ /**
+ * @var string the TrueType font file. This can be either a file path or path alias.
+ */
+ public $fontFile = '@yii/captcha/SpicyRice.ttf';
+ /**
+ * @var string the fixed verification code. When this property is set,
+ * [[getVerifyCode()]] will always return the value of this property.
+ * This is mainly used in automated tests where we want to be able to reproduce
+ * the same verification code each time we run the tests.
+ * If not set, it means the verification code will be randomly generated.
+ */
+ public $fixedVerifyCode;
+
+
+ /**
+ * Initializes the action.
+ * @throws InvalidConfigException if the font file does not exist.
+ */
+ public function init()
+ {
+ $this->fontFile = Yii::getAlias($this->fontFile);
+ if (!is_file($this->fontFile)) {
+ throw new InvalidConfigException("The font file does not exist: {$this->fontFile}");
+ }
+ }
+
+ /**
+ * Runs the action.
+ */
+ public function run()
+ {
+ if (isset($_GET[self::REFRESH_GET_VAR])) {
+ // AJAX request for regenerating code
+ $code = $this->getVerifyCode(true);
+ /** @var \yii\web\Controller $controller */
+ $controller = $this->controller;
+ return json_encode([
+ 'hash1' => $this->generateValidationHash($code),
+ 'hash2' => $this->generateValidationHash(strtolower($code)),
+ // we add a random 'v' parameter so that FireFox can refresh the image
+ // when src attribute of image tag is changed
+ 'url' => $controller->createUrl($this->id, ['v' => uniqid()]),
+ ]);
+ } else {
+ $this->setHttpHeaders();
+ return $this->renderImage($this->getVerifyCode());
+ }
+ }
+
+ /**
+ * Generates a hash code that can be used for client side validation.
+ * @param string $code the CAPTCHA code
+ * @return string a hash code generated from the CAPTCHA code
+ */
+ public function generateValidationHash($code)
+ {
+ for ($h = 0, $i = strlen($code) - 1; $i >= 0; --$i) {
+ $h += ord($code[$i]);
+ }
+ return $h;
+ }
+
+ /**
+ * Gets the verification code.
+ * @param boolean $regenerate whether the verification code should be regenerated.
+ * @return string the verification code.
+ */
+ public function getVerifyCode($regenerate = false)
+ {
+ if ($this->fixedVerifyCode !== null) {
+ return $this->fixedVerifyCode;
+ }
+
+ $session = Yii::$app->getSession();
+ $session->open();
+ $name = $this->getSessionKey();
+ if ($session[$name] === null || $regenerate) {
+ $session[$name] = $this->generateVerifyCode();
+ $session[$name . 'count'] = 1;
+ }
+ return $session[$name];
+ }
+
+ /**
+ * Validates the input to see if it matches the generated code.
+ * @param string $input user input
+ * @param boolean $caseSensitive whether the comparison should be case-sensitive
+ * @return boolean whether the input is valid
+ */
+ public function validate($input, $caseSensitive)
+ {
+ $code = $this->getVerifyCode();
+ $valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;
+ $session = Yii::$app->getSession();
+ $session->open();
+ $name = $this->getSessionKey() . 'count';
+ $session[$name] = $session[$name] + 1;
+ if ($valid || $session[$name] > $this->testLimit && $this->testLimit > 0) {
+ $this->getVerifyCode(true);
+ }
+ return $valid;
+ }
+
+ /**
+ * Generates a new verification code.
+ * @return string the generated verification code
+ */
+ protected function generateVerifyCode()
+ {
+ if ($this->minLength > $this->maxLength) {
+ $this->maxLength = $this->minLength;
+ }
+ if ($this->minLength < 3) {
+ $this->minLength = 3;
+ }
+ if ($this->maxLength > 20) {
+ $this->maxLength = 20;
+ }
+ $length = mt_rand($this->minLength, $this->maxLength);
+
+ $letters = 'bcdfghjklmnpqrstvwxyz';
+ $vowels = 'aeiou';
+ $code = '';
+ for ($i = 0; $i < $length; ++$i) {
+ if ($i % 2 && mt_rand(0, 10) > 2 || !($i % 2) && mt_rand(0, 10) > 9) {
+ $code .= $vowels[mt_rand(0, 4)];
+ } else {
+ $code .= $letters[mt_rand(0, 20)];
+ }
+ }
+
+ return $code;
+ }
+
+ /**
+ * Returns the session variable name used to store verification code.
+ * @return string the session variable name
+ */
+ protected function getSessionKey()
+ {
+ return '__captcha/' . $this->getUniqueId();
+ }
+
+ /**
+ * Renders the CAPTCHA image.
+ * @param string $code the verification code
+ * @return string image contents
+ */
+ protected function renderImage($code)
+ {
+ if (Captcha::checkRequirements() === 'gd') {
+ return $this->renderImageByGD($code);
+ } else {
+ return $this->renderImageByImagick($code);
+ }
+ }
+
+ /**
+ * Renders the CAPTCHA image based on the code using GD library.
+ * @param string $code the verification code
+ * @return string image contents
+ */
+ protected function renderImageByGD($code)
+ {
+ $image = imagecreatetruecolor($this->width, $this->height);
+
+ $backColor = imagecolorallocate($image,
+ (int)($this->backColor % 0x1000000 / 0x10000),
+ (int)($this->backColor % 0x10000 / 0x100),
+ $this->backColor % 0x100);
+ imagefilledrectangle($image, 0, 0, $this->width, $this->height, $backColor);
+ imagecolordeallocate($image, $backColor);
+
+ if ($this->transparent) {
+ imagecolortransparent($image, $backColor);
+ }
+
+ $foreColor = imagecolorallocate($image,
+ (int)($this->foreColor % 0x1000000 / 0x10000),
+ (int)($this->foreColor % 0x10000 / 0x100),
+ $this->foreColor % 0x100);
+
+ $length = strlen($code);
+ $box = imagettfbbox(30, 0, $this->fontFile, $code);
+ $w = $box[4] - $box[0] + $this->offset * ($length - 1);
+ $h = $box[1] - $box[5];
+ $scale = min(($this->width - $this->padding * 2) / $w, ($this->height - $this->padding * 2) / $h);
+ $x = 10;
+ $y = round($this->height * 27 / 40);
+ for ($i = 0; $i < $length; ++$i) {
+ $fontSize = (int)(rand(26, 32) * $scale * 0.8);
+ $angle = rand(-10, 10);
+ $letter = $code[$i];
+ $box = imagettftext($image, $fontSize, $angle, $x, $y, $foreColor, $this->fontFile, $letter);
+ $x = $box[2] + $this->offset;
+ }
+
+ imagecolordeallocate($image, $foreColor);
+
+ ob_start();
+ imagepng($image);
+ imagedestroy($image);
+ return ob_get_clean();
+ }
+
+ /**
+ * Renders the CAPTCHA image based on the code using ImageMagick library.
+ * @param string $code the verification code
+ * @return \Imagick image instance. Can be used as string. In this case it will contain image contents.
+ */
+ protected function renderImageByImagick($code)
+ {
+ $backColor = $this->transparent ? new \ImagickPixel('transparent') : new \ImagickPixel('#' . dechex($this->backColor));
+ $foreColor = new \ImagickPixel('#' . dechex($this->foreColor));
+
+ $image = new \Imagick();
+ $image->newImage($this->width, $this->height, $backColor);
+
+ $draw = new \ImagickDraw();
+ $draw->setFont($this->fontFile);
+ $draw->setFontSize(30);
+ $fontMetrics = $image->queryFontMetrics($draw, $code);
+
+ $length = strlen($code);
+ $w = (int)($fontMetrics['textWidth']) - 8 + $this->offset * ($length - 1);
+ $h = (int)($fontMetrics['textHeight']) - 8;
+ $scale = min(($this->width - $this->padding * 2) / $w, ($this->height - $this->padding * 2) / $h);
+ $x = 10;
+ $y = round($this->height * 27 / 40);
+ for ($i = 0; $i < $length; ++$i) {
+ $draw = new \ImagickDraw();
+ $draw->setFont($this->fontFile);
+ $draw->setFontSize((int)(rand(26, 32) * $scale * 0.8));
+ $draw->setFillColor($foreColor);
+ $image->annotateImage($draw, $x, $y, rand(-10, 10), $code[$i]);
+ $fontMetrics = $image->queryFontMetrics($draw, $code[$i]);
+ $x += (int)($fontMetrics['textWidth']) + $this->offset;
+ }
+
+ $image->setImageFormat('png');
+ return $image;
+ }
+
+ /**
+ * Sets the HTTP headers needed by image response.
+ */
+ protected function setHttpHeaders()
+ {
+ Yii::$app->getResponse()->getHeaders()
+ ->set('Pragma', 'public')
+ ->set('Expires', '0')
+ ->set('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
+ ->set('Content-Transfer-Encoding', 'binary')
+ ->set('Content-type', 'image/png');
+ }
+}
diff --git a/framework/yii/captcha/CaptchaAsset.php b/framework/yii/captcha/CaptchaAsset.php
new file mode 100644
index 0000000..4fc722f
--- /dev/null
+++ b/framework/yii/captcha/CaptchaAsset.php
@@ -0,0 +1,27 @@
+
+ * @since 2.0
+ */
+class CaptchaAsset extends AssetBundle
+{
+ public $sourcePath = '@yii/assets';
+ public $js = [
+ 'yii.captcha.js',
+ ];
+ public $depends = [
+ 'yii\web\YiiAsset',
+ ];
+}
diff --git a/framework/yii/captcha/CaptchaValidator.php b/framework/yii/captcha/CaptchaValidator.php
new file mode 100644
index 0000000..57665ec
--- /dev/null
+++ b/framework/yii/captcha/CaptchaValidator.php
@@ -0,0 +1,107 @@
+
+ * @since 2.0
+ */
+class CaptchaValidator extends Validator
+{
+ /**
+ * @var boolean whether to skip this validator if the input is empty.
+ */
+ public $skipOnEmpty = false;
+ /**
+ * @var boolean whether the comparison is case sensitive. Defaults to false.
+ */
+ public $caseSensitive = false;
+ /**
+ * @var string the route of the controller action that renders the CAPTCHA image.
+ */
+ public $captchaAction = 'site/captcha';
+
+
+ /**
+ * @inheritdoc
+ */
+ public function init()
+ {
+ parent::init();
+ if ($this->message === null) {
+ $this->message = Yii::t('yii', 'The verification code is incorrect.');
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function validateValue($value)
+ {
+ $captcha = $this->createCaptchaAction();
+ $valid = !is_array($value) && $captcha->validate($value, $this->caseSensitive);
+ return $valid ? null : [$this->message, []];
+ }
+
+ /**
+ * Creates the CAPTCHA action object from the route specified by [[captchaAction]].
+ * @return \yii\captcha\CaptchaAction the action object
+ * @throws InvalidConfigException
+ */
+ public function createCaptchaAction()
+ {
+ $ca = Yii::$app->createController($this->captchaAction);
+ if ($ca !== false) {
+ /** @var \yii\base\Controller $controller */
+ list($controller, $actionID) = $ca;
+ $action = $controller->createAction($actionID);
+ if ($action !== null) {
+ return $action;
+ }
+ }
+ throw new InvalidConfigException('Invalid CAPTCHA action ID: ' . $this->captchaAction);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function clientValidateAttribute($object, $attribute, $view)
+ {
+ $captcha = $this->createCaptchaAction();
+ $code = $captcha->getVerifyCode(false);
+ $hash = $captcha->generateValidationHash($this->caseSensitive ? $code : strtolower($code));
+ $options = [
+ 'hash' => $hash,
+ 'hashKey' => 'yiiCaptcha/' . $this->captchaAction,
+ 'caseSensitive' => $this->caseSensitive,
+ 'message' => strtr($this->message, [
+ '{attribute}' => $object->getAttributeLabel($attribute),
+ ]),
+ ];
+ if ($this->skipOnEmpty) {
+ $options['skipOnEmpty'] = 1;
+ }
+
+ ValidationAsset::register($view);
+ return 'yii.validation.captcha(value, messages, ' . json_encode($options) . ');';
+ }
+}
diff --git a/framework/yii/captcha/SpicyRice.md b/framework/yii/captcha/SpicyRice.md
new file mode 100644
index 0000000..7049bd1
--- /dev/null
+++ b/framework/yii/captcha/SpicyRice.md
@@ -0,0 +1,11 @@
+## Spicy Rice font
+
+* **Author:** Brian J. Bonislawsky, Astigmatic (AOETI, Astigmatic One Eye Typographic Institute)
+* **License:** SIL Open Font License (OFL), version 1.1, [notes and FAQ](http://scripts.sil.org/OFL)
+
+## Links
+
+* [Astigmatic](http://www.astigmatic.com/)
+* [Google WebFonts](http://www.google.com/webfonts/specimen/Spicy+Rice)
+* [fontsquirrel.com](http://www.fontsquirrel.com/fonts/spicy-rice)
+* [fontspace.com](http://www.fontspace.com/astigmatic-one-eye-typographic-institute/spicy-rice)
diff --git a/framework/yii/web/SpicyRice.ttf b/framework/yii/captcha/SpicyRice.ttf
similarity index 100%
rename from framework/yii/web/SpicyRice.ttf
rename to framework/yii/captcha/SpicyRice.ttf
diff --git a/framework/yii/classes.php b/framework/yii/classes.php
new file mode 100644
index 0000000..9606930
--- /dev/null
+++ b/framework/yii/classes.php
@@ -0,0 +1,253 @@
+ YII_PATH . '/base/Action.php',
+ 'yii\base\ActionEvent' => YII_PATH . '/base/ActionEvent.php',
+ 'yii\base\ActionFilter' => YII_PATH . '/base/ActionFilter.php',
+ 'yii\base\Application' => YII_PATH . '/base/Application.php',
+ 'yii\base\Arrayable' => YII_PATH . '/base/Arrayable.php',
+ 'yii\base\Behavior' => YII_PATH . '/base/Behavior.php',
+ 'yii\base\Component' => YII_PATH . '/base/Component.php',
+ 'yii\base\Controller' => YII_PATH . '/base/Controller.php',
+ 'yii\base\ErrorException' => YII_PATH . '/base/ErrorException.php',
+ 'yii\base\ErrorHandler' => YII_PATH . '/base/ErrorHandler.php',
+ 'yii\base\Event' => YII_PATH . '/base/Event.php',
+ 'yii\base\Exception' => YII_PATH . '/base/Exception.php',
+ 'yii\base\Extension' => YII_PATH . '/base/Extension.php',
+ 'yii\base\Formatter' => YII_PATH . '/base/Formatter.php',
+ 'yii\base\InlineAction' => YII_PATH . '/base/InlineAction.php',
+ 'yii\base\InvalidCallException' => YII_PATH . '/base/InvalidCallException.php',
+ 'yii\base\InvalidConfigException' => YII_PATH . '/base/InvalidConfigException.php',
+ 'yii\base\InvalidParamException' => YII_PATH . '/base/InvalidParamException.php',
+ 'yii\base\InvalidRouteException' => YII_PATH . '/base/InvalidRouteException.php',
+ 'yii\base\Model' => YII_PATH . '/base/Model.php',
+ 'yii\base\ModelEvent' => YII_PATH . '/base/ModelEvent.php',
+ 'yii\base\Module' => YII_PATH . '/base/Module.php',
+ 'yii\base\NotSupportedException' => YII_PATH . '/base/NotSupportedException.php',
+ 'yii\base\Object' => YII_PATH . '/base/Object.php',
+ 'yii\base\Request' => YII_PATH . '/base/Request.php',
+ 'yii\base\Response' => YII_PATH . '/base/Response.php',
+ 'yii\base\Theme' => YII_PATH . '/base/Theme.php',
+ 'yii\base\UnknownClassException' => YII_PATH . '/base/UnknownClassException.php',
+ 'yii\base\UnknownMethodException' => YII_PATH . '/base/UnknownMethodException.php',
+ 'yii\base\UnknownPropertyException' => YII_PATH . '/base/UnknownPropertyException.php',
+ 'yii\base\UserException' => YII_PATH . '/base/UserException.php',
+ 'yii\base\View' => YII_PATH . '/base/View.php',
+ 'yii\base\ViewContextInterface' => YII_PATH . '/base/ViewContextInterface.php',
+ 'yii\base\ViewEvent' => YII_PATH . '/base/ViewEvent.php',
+ 'yii\base\ViewRenderer' => YII_PATH . '/base/ViewRenderer.php',
+ 'yii\base\Widget' => YII_PATH . '/base/Widget.php',
+ 'yii\behaviors\AutoTimestamp' => YII_PATH . '/behaviors/AutoTimestamp.php',
+ 'yii\caching\ApcCache' => YII_PATH . '/caching/ApcCache.php',
+ 'yii\caching\Cache' => YII_PATH . '/caching/Cache.php',
+ 'yii\caching\ChainedDependency' => YII_PATH . '/caching/ChainedDependency.php',
+ 'yii\caching\DbCache' => YII_PATH . '/caching/DbCache.php',
+ 'yii\caching\DbDependency' => YII_PATH . '/caching/DbDependency.php',
+ 'yii\caching\Dependency' => YII_PATH . '/caching/Dependency.php',
+ 'yii\caching\DummyCache' => YII_PATH . '/caching/DummyCache.php',
+ 'yii\caching\ExpressionDependency' => YII_PATH . '/caching/ExpressionDependency.php',
+ 'yii\caching\FileCache' => YII_PATH . '/caching/FileCache.php',
+ 'yii\caching\FileDependency' => YII_PATH . '/caching/FileDependency.php',
+ 'yii\caching\GroupDependency' => YII_PATH . '/caching/GroupDependency.php',
+ 'yii\caching\MemCache' => YII_PATH . '/caching/MemCache.php',
+ 'yii\caching\MemCacheServer' => YII_PATH . '/caching/MemCacheServer.php',
+ 'yii\caching\WinCache' => YII_PATH . '/caching/WinCache.php',
+ 'yii\caching\XCache' => YII_PATH . '/caching/XCache.php',
+ 'yii\caching\ZendDataCache' => YII_PATH . '/caching/ZendDataCache.php',
+ 'yii\captcha\Captcha' => YII_PATH . '/captcha/Captcha.php',
+ 'yii\captcha\CaptchaAction' => YII_PATH . '/captcha/CaptchaAction.php',
+ 'yii\captcha\CaptchaAsset' => YII_PATH . '/captcha/CaptchaAsset.php',
+ 'yii\captcha\CaptchaValidator' => YII_PATH . '/captcha/CaptchaValidator.php',
+ 'yii\data\ActiveDataProvider' => YII_PATH . '/data/ActiveDataProvider.php',
+ 'yii\data\ArrayDataProvider' => YII_PATH . '/data/ArrayDataProvider.php',
+ 'yii\data\BaseDataProvider' => YII_PATH . '/data/BaseDataProvider.php',
+ 'yii\data\DataProviderInterface' => YII_PATH . '/data/DataProviderInterface.php',
+ 'yii\data\Pagination' => YII_PATH . '/data/Pagination.php',
+ 'yii\data\Sort' => YII_PATH . '/data/Sort.php',
+ 'yii\data\SqlDataProvider' => YII_PATH . '/data/SqlDataProvider.php',
+ 'yii\db\ActiveQuery' => YII_PATH . '/db/ActiveQuery.php',
+ 'yii\db\ActiveQueryInterface' => YII_PATH . '/db/ActiveQueryInterface.php',
+ 'yii\db\ActiveQueryTrait' => YII_PATH . '/db/ActiveQueryTrait.php',
+ 'yii\db\ActiveRecord' => YII_PATH . '/db/ActiveRecord.php',
+ 'yii\db\ActiveRecordInterface' => YII_PATH . '/db/ActiveRecordInterface.php',
+ 'yii\db\ActiveRelation' => YII_PATH . '/db/ActiveRelation.php',
+ 'yii\db\ActiveRelationInterface' => YII_PATH . '/db/ActiveRelationInterface.php',
+ 'yii\db\ActiveRelationTrait' => YII_PATH . '/db/ActiveRelationTrait.php',
+ 'yii\db\BaseActiveRecord' => YII_PATH . '/db/BaseActiveRecord.php',
+ 'yii\db\ColumnSchema' => YII_PATH . '/db/ColumnSchema.php',
+ 'yii\db\Command' => YII_PATH . '/db/Command.php',
+ 'yii\db\Connection' => YII_PATH . '/db/Connection.php',
+ 'yii\db\DataReader' => YII_PATH . '/db/DataReader.php',
+ 'yii\db\Exception' => YII_PATH . '/db/Exception.php',
+ 'yii\db\Expression' => YII_PATH . '/db/Expression.php',
+ 'yii\db\Migration' => YII_PATH . '/db/Migration.php',
+ 'yii\db\Query' => YII_PATH . '/db/Query.php',
+ 'yii\db\QueryBuilder' => YII_PATH . '/db/QueryBuilder.php',
+ 'yii\db\QueryInterface' => YII_PATH . '/db/QueryInterface.php',
+ 'yii\db\QueryTrait' => YII_PATH . '/db/QueryTrait.php',
+ 'yii\db\Schema' => YII_PATH . '/db/Schema.php',
+ 'yii\db\StaleObjectException' => YII_PATH . '/db/StaleObjectException.php',
+ 'yii\db\TableSchema' => YII_PATH . '/db/TableSchema.php',
+ 'yii\db\Transaction' => YII_PATH . '/db/Transaction.php',
+ 'yii\db\cubrid\QueryBuilder' => YII_PATH . '/db/cubrid/QueryBuilder.php',
+ 'yii\db\cubrid\Schema' => YII_PATH . '/db/cubrid/Schema.php',
+ 'yii\db\mssql\PDO' => YII_PATH . '/db/mssql/PDO.php',
+ 'yii\db\mssql\QueryBuilder' => YII_PATH . '/db/mssql/QueryBuilder.php',
+ 'yii\db\mssql\Schema' => YII_PATH . '/db/mssql/Schema.php',
+ 'yii\db\mssql\SqlsrvPDO' => YII_PATH . '/db/mssql/SqlsrvPDO.php',
+ 'yii\db\mssql\TableSchema' => YII_PATH . '/db/mssql/TableSchema.php',
+ 'yii\db\mysql\QueryBuilder' => YII_PATH . '/db/mysql/QueryBuilder.php',
+ 'yii\db\mysql\Schema' => YII_PATH . '/db/mysql/Schema.php',
+ 'yii\db\pgsql\QueryBuilder' => YII_PATH . '/db/pgsql/QueryBuilder.php',
+ 'yii\db\pgsql\Schema' => YII_PATH . '/db/pgsql/Schema.php',
+ 'yii\db\sqlite\QueryBuilder' => YII_PATH . '/db/sqlite/QueryBuilder.php',
+ 'yii\db\sqlite\Schema' => YII_PATH . '/db/sqlite/Schema.php',
+ 'yii\grid\ActionColumn' => YII_PATH . '/grid/ActionColumn.php',
+ 'yii\grid\CheckboxColumn' => YII_PATH . '/grid/CheckboxColumn.php',
+ 'yii\grid\Column' => YII_PATH . '/grid/Column.php',
+ 'yii\grid\DataColumn' => YII_PATH . '/grid/DataColumn.php',
+ 'yii\grid\GridView' => YII_PATH . '/grid/GridView.php',
+ 'yii\grid\GridViewAsset' => YII_PATH . '/grid/GridViewAsset.php',
+ 'yii\grid\SerialColumn' => YII_PATH . '/grid/SerialColumn.php',
+ 'yii\helpers\ArrayHelper' => YII_PATH . '/helpers/ArrayHelper.php',
+ 'yii\helpers\BaseArrayHelper' => YII_PATH . '/helpers/BaseArrayHelper.php',
+ 'yii\helpers\BaseConsole' => YII_PATH . '/helpers/BaseConsole.php',
+ 'yii\helpers\BaseFileHelper' => YII_PATH . '/helpers/BaseFileHelper.php',
+ 'yii\helpers\BaseHtml' => YII_PATH . '/helpers/BaseHtml.php',
+ 'yii\helpers\BaseHtmlPurifier' => YII_PATH . '/helpers/BaseHtmlPurifier.php',
+ 'yii\helpers\BaseInflector' => YII_PATH . '/helpers/BaseInflector.php',
+ 'yii\helpers\BaseJson' => YII_PATH . '/helpers/BaseJson.php',
+ 'yii\helpers\BaseMarkdown' => YII_PATH . '/helpers/BaseMarkdown.php',
+ 'yii\helpers\BaseSecurity' => YII_PATH . '/helpers/BaseSecurity.php',
+ 'yii\helpers\BaseStringHelper' => YII_PATH . '/helpers/BaseStringHelper.php',
+ 'yii\helpers\BaseVarDumper' => YII_PATH . '/helpers/BaseVarDumper.php',
+ 'yii\helpers\Console' => YII_PATH . '/helpers/Console.php',
+ 'yii\helpers\FileHelper' => YII_PATH . '/helpers/FileHelper.php',
+ 'yii\helpers\Html' => YII_PATH . '/helpers/Html.php',
+ 'yii\helpers\HtmlPurifier' => YII_PATH . '/helpers/HtmlPurifier.php',
+ 'yii\helpers\Inflector' => YII_PATH . '/helpers/Inflector.php',
+ 'yii\helpers\Json' => YII_PATH . '/helpers/Json.php',
+ 'yii\helpers\Markdown' => YII_PATH . '/helpers/Markdown.php',
+ 'yii\helpers\Security' => YII_PATH . '/helpers/Security.php',
+ 'yii\helpers\StringHelper' => YII_PATH . '/helpers/StringHelper.php',
+ 'yii\helpers\VarDumper' => YII_PATH . '/helpers/VarDumper.php',
+ 'yii\i18n\DbMessageSource' => YII_PATH . '/i18n/DbMessageSource.php',
+ 'yii\i18n\Formatter' => YII_PATH . '/i18n/Formatter.php',
+ 'yii\i18n\GettextFile' => YII_PATH . '/i18n/GettextFile.php',
+ 'yii\i18n\GettextMessageSource' => YII_PATH . '/i18n/GettextMessageSource.php',
+ 'yii\i18n\GettextMoFile' => YII_PATH . '/i18n/GettextMoFile.php',
+ 'yii\i18n\GettextPoFile' => YII_PATH . '/i18n/GettextPoFile.php',
+ 'yii\i18n\I18N' => YII_PATH . '/i18n/I18N.php',
+ 'yii\i18n\MessageFormatter' => YII_PATH . '/i18n/MessageFormatter.php',
+ 'yii\i18n\MessageSource' => YII_PATH . '/i18n/MessageSource.php',
+ 'yii\i18n\MissingTranslationEvent' => YII_PATH . '/i18n/MissingTranslationEvent.php',
+ 'yii\i18n\PhpMessageSource' => YII_PATH . '/i18n/PhpMessageSource.php',
+ 'yii\log\DbTarget' => YII_PATH . '/log/DbTarget.php',
+ 'yii\log\EmailTarget' => YII_PATH . '/log/EmailTarget.php',
+ 'yii\log\FileTarget' => YII_PATH . '/log/FileTarget.php',
+ 'yii\log\Logger' => YII_PATH . '/log/Logger.php',
+ 'yii\log\Target' => YII_PATH . '/log/Target.php',
+ 'yii\mail\BaseMailer' => YII_PATH . '/mail/BaseMailer.php',
+ 'yii\mail\BaseMessage' => YII_PATH . '/mail/BaseMessage.php',
+ 'yii\mail\MailerInterface' => YII_PATH . '/mail/MailerInterface.php',
+ 'yii\mail\MessageInterface' => YII_PATH . '/mail/MessageInterface.php',
+ 'yii\mutex\DbMutex' => YII_PATH . '/mutex/DbMutex.php',
+ 'yii\mutex\FileMutex' => YII_PATH . '/mutex/FileMutex.php',
+ 'yii\mutex\Mutex' => YII_PATH . '/mutex/Mutex.php',
+ 'yii\mutex\MysqlMutex' => YII_PATH . '/mutex/MysqlMutex.php',
+ 'yii\rbac\Assignment' => YII_PATH . '/rbac/Assignment.php',
+ 'yii\rbac\DbManager' => YII_PATH . '/rbac/DbManager.php',
+ 'yii\rbac\Item' => YII_PATH . '/rbac/Item.php',
+ 'yii\rbac\Manager' => YII_PATH . '/rbac/Manager.php',
+ 'yii\rbac\PhpManager' => YII_PATH . '/rbac/PhpManager.php',
+ 'yii\requirements\YiiRequirementChecker' => YII_PATH . '/requirements/YiiRequirementChecker.php',
+ 'yii\test\DbFixtureManager' => YII_PATH . '/test/DbFixtureManager.php',
+ 'yii\test\DbTestTrait' => YII_PATH . '/test/DbTestTrait.php',
+ 'yii\validators\BooleanValidator' => YII_PATH . '/validators/BooleanValidator.php',
+ 'yii\validators\CompareValidator' => YII_PATH . '/validators/CompareValidator.php',
+ 'yii\validators\DateValidator' => YII_PATH . '/validators/DateValidator.php',
+ 'yii\validators\DefaultValueValidator' => YII_PATH . '/validators/DefaultValueValidator.php',
+ 'yii\validators\EmailValidator' => YII_PATH . '/validators/EmailValidator.php',
+ 'yii\validators\ExistValidator' => YII_PATH . '/validators/ExistValidator.php',
+ 'yii\validators\FileValidator' => YII_PATH . '/validators/FileValidator.php',
+ 'yii\validators\FilterValidator' => YII_PATH . '/validators/FilterValidator.php',
+ 'yii\validators\ImageValidator' => YII_PATH . '/validators/ImageValidator.php',
+ 'yii\validators\InlineValidator' => YII_PATH . '/validators/InlineValidator.php',
+ 'yii\validators\NumberValidator' => YII_PATH . '/validators/NumberValidator.php',
+ 'yii\validators\PunycodeAsset' => YII_PATH . '/validators/PunycodeAsset.php',
+ 'yii\validators\RangeValidator' => YII_PATH . '/validators/RangeValidator.php',
+ 'yii\validators\RegularExpressionValidator' => YII_PATH . '/validators/RegularExpressionValidator.php',
+ 'yii\validators\RequiredValidator' => YII_PATH . '/validators/RequiredValidator.php',
+ 'yii\validators\SafeValidator' => YII_PATH . '/validators/SafeValidator.php',
+ 'yii\validators\StringValidator' => YII_PATH . '/validators/StringValidator.php',
+ 'yii\validators\UniqueValidator' => YII_PATH . '/validators/UniqueValidator.php',
+ 'yii\validators\UrlValidator' => YII_PATH . '/validators/UrlValidator.php',
+ 'yii\validators\ValidationAsset' => YII_PATH . '/validators/ValidationAsset.php',
+ 'yii\validators\Validator' => YII_PATH . '/validators/Validator.php',
+ 'yii\web\AccessControl' => YII_PATH . '/web/AccessControl.php',
+ 'yii\web\AccessDeniedHttpException' => YII_PATH . '/web/AccessDeniedHttpException.php',
+ 'yii\web\AccessRule' => YII_PATH . '/web/AccessRule.php',
+ 'yii\web\Application' => YII_PATH . '/web/Application.php',
+ 'yii\web\AssetBundle' => YII_PATH . '/web/AssetBundle.php',
+ 'yii\web\AssetConverter' => YII_PATH . '/web/AssetConverter.php',
+ 'yii\web\AssetConverterInterface' => YII_PATH . '/web/AssetConverterInterface.php',
+ 'yii\web\AssetManager' => YII_PATH . '/web/AssetManager.php',
+ 'yii\web\BadRequestHttpException' => YII_PATH . '/web/BadRequestHttpException.php',
+ 'yii\web\CacheSession' => YII_PATH . '/web/CacheSession.php',
+ 'yii\web\Controller' => YII_PATH . '/web/Controller.php',
+ 'yii\web\Cookie' => YII_PATH . '/web/Cookie.php',
+ 'yii\web\CookieCollection' => YII_PATH . '/web/CookieCollection.php',
+ 'yii\web\DbSession' => YII_PATH . '/web/DbSession.php',
+ 'yii\web\ErrorAction' => YII_PATH . '/web/ErrorAction.php',
+ 'yii\web\HeaderCollection' => YII_PATH . '/web/HeaderCollection.php',
+ 'yii\web\HttpCache' => YII_PATH . '/web/HttpCache.php',
+ 'yii\web\HttpException' => YII_PATH . '/web/HttpException.php',
+ 'yii\web\IdentityInterface' => YII_PATH . '/web/IdentityInterface.php',
+ 'yii\web\JqueryAsset' => YII_PATH . '/web/JqueryAsset.php',
+ 'yii\web\JsExpression' => YII_PATH . '/web/JsExpression.php',
+ 'yii\web\MethodNotAllowedHttpException' => YII_PATH . '/web/MethodNotAllowedHttpException.php',
+ 'yii\web\NotFoundHttpException' => YII_PATH . '/web/NotFoundHttpException.php',
+ 'yii\web\PageCache' => YII_PATH . '/web/PageCache.php',
+ 'yii\web\Request' => YII_PATH . '/web/Request.php',
+ 'yii\web\Response' => YII_PATH . '/web/Response.php',
+ 'yii\web\ResponseEvent' => YII_PATH . '/web/ResponseEvent.php',
+ 'yii\web\ResponseFormatterInterface' => YII_PATH . '/web/ResponseFormatterInterface.php',
+ 'yii\web\Session' => YII_PATH . '/web/Session.php',
+ 'yii\web\SessionIterator' => YII_PATH . '/web/SessionIterator.php',
+ 'yii\web\UploadedFile' => YII_PATH . '/web/UploadedFile.php',
+ 'yii\web\UrlManager' => YII_PATH . '/web/UrlManager.php',
+ 'yii\web\UrlRule' => YII_PATH . '/web/UrlRule.php',
+ 'yii\web\User' => YII_PATH . '/web/User.php',
+ 'yii\web\UserEvent' => YII_PATH . '/web/UserEvent.php',
+ 'yii\web\VerbFilter' => YII_PATH . '/web/VerbFilter.php',
+ 'yii\web\View' => YII_PATH . '/web/View.php',
+ 'yii\web\XmlResponseFormatter' => YII_PATH . '/web/XmlResponseFormatter.php',
+ 'yii\web\YiiAsset' => YII_PATH . '/web/YiiAsset.php',
+ 'yii\widgets\ActiveField' => YII_PATH . '/widgets/ActiveField.php',
+ 'yii\widgets\ActiveForm' => YII_PATH . '/widgets/ActiveForm.php',
+ 'yii\widgets\ActiveFormAsset' => YII_PATH . '/widgets/ActiveFormAsset.php',
+ 'yii\widgets\BaseListView' => YII_PATH . '/widgets/BaseListView.php',
+ 'yii\widgets\Block' => YII_PATH . '/widgets/Block.php',
+ 'yii\widgets\Breadcrumbs' => YII_PATH . '/widgets/Breadcrumbs.php',
+ 'yii\widgets\ContentDecorator' => YII_PATH . '/widgets/ContentDecorator.php',
+ 'yii\widgets\DetailView' => YII_PATH . '/widgets/DetailView.php',
+ 'yii\widgets\FragmentCache' => YII_PATH . '/widgets/FragmentCache.php',
+ 'yii\widgets\InputWidget' => YII_PATH . '/widgets/InputWidget.php',
+ 'yii\widgets\LinkPager' => YII_PATH . '/widgets/LinkPager.php',
+ 'yii\widgets\LinkSorter' => YII_PATH . '/widgets/LinkSorter.php',
+ 'yii\widgets\ListView' => YII_PATH . '/widgets/ListView.php',
+ 'yii\widgets\MaskedInput' => YII_PATH . '/widgets/MaskedInput.php',
+ 'yii\widgets\MaskedInputAsset' => YII_PATH . '/widgets/MaskedInputAsset.php',
+ 'yii\widgets\Menu' => YII_PATH . '/widgets/Menu.php',
+ 'yii\widgets\Spaceless' => YII_PATH . '/widgets/Spaceless.php',
+];
diff --git a/framework/yii/console/Application.php b/framework/yii/console/Application.php
index 6cc114a..6701304 100644
--- a/framework/yii/console/Application.php
+++ b/framework/yii/console/Application.php
@@ -9,6 +9,7 @@
namespace yii\console;
+use Yii;
use yii\base\InvalidRouteException;
/**
@@ -45,6 +46,8 @@ use yii\base\InvalidRouteException;
* yii help
* ~~~
*
+ * @property Response $response The response component. This property is read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -60,6 +63,10 @@ class Application extends \yii\base\Application
* Defaults to true.
*/
public $enableCoreCommands = true;
+ /**
+ * @var Controller the currently active controller instance
+ */
+ public $controller;
/**
* Initialize the application.
@@ -81,24 +88,34 @@ class Application extends \yii\base\Application
}
/**
- * Processes the request.
- * The request is represented in terms of a controller route and action parameters.
- * @return integer the exit status of the controller action (0 means normal, non-zero values mean abnormal)
- * @throws Exception if the script is not running from the command line
+ * Handles the specified request.
+ * @param Request $request the request to be handled
+ * @return Response the resulting response
*/
- public function processRequest()
+ public function handleRequest($request)
{
- /** @var $request Request */
- $request = $this->getRequest();
- if ($request->getIsConsoleRequest()) {
- list ($route, $params) = $request->resolve();
- return $this->runAction($route, $params);
+ list ($route, $params) = $request->resolve();
+ $this->requestedRoute = $route;
+ $result = $this->runAction($route, $params);
+ if ($result instanceof Response) {
+ return $result;
} else {
- throw new Exception(\Yii::t('yii', 'This script must be run from the command line.'));
+ $response = $this->getResponse();
+ $response->exitStatus = (int)$result;
+ return $response;
}
}
/**
+ * Returns the response component.
+ * @return Response the response component
+ */
+ public function getResponse()
+ {
+ return $this->getComponent('response');
+ }
+
+ /**
* Runs a controller action specified by a route.
* This method parses the specified route and creates the corresponding child module(s), controller and action
* instances. It then calls [[Controller::runAction()]] to run the action with the given parameters.
@@ -108,12 +125,12 @@ class Application extends \yii\base\Application
* @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal.
* @throws Exception if the route is invalid
*/
- public function runAction($route, $params = array())
+ public function runAction($route, $params = [])
{
try {
return parent::runAction($route, $params);
} catch (InvalidRouteException $e) {
- throw new Exception(\Yii::t('yii', 'Unknown command "{command}".', array('{command}' => $route)), 0, $e);
+ throw new Exception(Yii::t('yii', 'Unknown command "{command}".', ['command' => $route]), 0, $e);
}
}
@@ -123,13 +140,13 @@ class Application extends \yii\base\Application
*/
public function coreCommands()
{
- return array(
+ return [
'message' => 'yii\console\controllers\MessageController',
'help' => 'yii\console\controllers\HelpController',
'migrate' => 'yii\console\controllers\MigrateController',
'cache' => 'yii\console\controllers\CacheController',
'asset' => 'yii\console\controllers\AssetController',
- );
+ ];
}
/**
@@ -139,13 +156,9 @@ class Application extends \yii\base\Application
public function registerCoreComponents()
{
parent::registerCoreComponents();
- $this->setComponents(array(
- 'request' => array(
- 'class' => 'yii\console\Request',
- ),
- 'response' => array(
- 'class' => 'yii\console\Response',
- ),
- ));
+ $this->setComponents([
+ 'request' => ['class' => 'yii\console\Request'],
+ 'response' => ['class' => 'yii\console\Response'],
+ ]);
}
}
diff --git a/framework/yii/console/Controller.php b/framework/yii/console/Controller.php
index 22ec39f..47ac4b0 100644
--- a/framework/yii/console/Controller.php
+++ b/framework/yii/console/Controller.php
@@ -30,40 +30,28 @@ use yii\helpers\Console;
class Controller extends \yii\base\Controller
{
/**
- * @var boolean whether the call of [[confirm()]] requires a user input.
- * If false, [[confirm()]] will always return true no matter what user enters or not.
+ * @var boolean whether to run the command interactively.
*/
public $interactive = true;
/**
- * @var boolean whether to enable ANSI style in output.
- * Defaults to null meaning auto-detect.
+ * @var boolean whether to enable ANSI color in the output.
+ * If not set, ANSI color will only be enabled for terminals that support it.
*/
- private $_colors;
+ public $color;
/**
- * Whether to enable ANSI style in output.
+ * Returns a value indicating whether ANSI color is enabled.
*
- * Setting this will affect [[ansiFormat()]], [[stdout()]] and [[stderr()]].
- * If not set it will be auto detected using [[yii\helpers\Console::streamSupportsAnsiColors()]] with STDOUT
- * for [[ansiFormat()]] and [[stdout()]] and STDERR for [[stderr()]].
- * @param resource $stream
+ * ANSI color is enabled only if [[color]] is set true or is not set
+ * and the terminal supports ANSI color.
+ *
+ * @param resource $stream the stream to check.
* @return boolean Whether to enable ANSI style in output.
*/
- public function getColors($stream = STDOUT)
- {
- if ($this->_colors === null) {
- return Console::streamSupportsAnsiColors($stream);
- }
- return $this->_colors;
- }
-
- /**
- * Whether to enable ANSI style in output.
- */
- public function setColors($value)
+ public function isColorEnabled($stream = STDOUT)
{
- $this->_colors = (bool) $value;
+ return $this->color === null ? Console::streamSupportsAnsiColors($stream) : $this->color;
}
/**
@@ -75,7 +63,7 @@ class Controller extends \yii\base\Controller
* @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
* @see createAction
*/
- public function runAction($id, $params = array())
+ public function runAction($id, $params = [])
{
if (!empty($params)) {
$options = $this->globalOptions();
@@ -101,31 +89,27 @@ class Controller extends \yii\base\Controller
*/
public function bindActionParams($action, $params)
{
+ $args = [];
if (!empty($params)) {
$options = $this->globalOptions();
foreach ($params as $name => $value) {
if (in_array($name, $options, true)) {
$this->$name = $value;
- unset($params[$name]);
+ } elseif (is_int($name)) {
+ $args[] = $value;
+ } else {
+ throw new Exception(Yii::t('yii', 'Unknown option: --{name}', ['name' => $name]));
}
}
}
- $args = isset($params[Request::ANONYMOUS_PARAMS]) ? $params[Request::ANONYMOUS_PARAMS] : array();
- unset($params[Request::ANONYMOUS_PARAMS]);
- if (!empty($params)) {
- throw new Exception(Yii::t('yii', 'Unknown options: {params}', array(
- '{params}' => implode(', ', array_keys($params)),
- )));
- }
-
if ($action instanceof InlineAction) {
$method = new \ReflectionMethod($this, $action->actionMethod);
} else {
$method = new \ReflectionMethod($action, 'run');
}
- $missing = array();
+ $missing = [];
foreach ($method->getParameters() as $i => $param) {
$name = $param->getName();
if (!isset($args[$i])) {
@@ -138,9 +122,7 @@ class Controller extends \yii\base\Controller
}
if (!empty($missing)) {
- throw new Exception(Yii::t('yii', 'Missing required arguments: {params}', array(
- '{params}' => implode(', ', $missing),
- )));
+ throw new Exception(Yii::t('yii', 'Missing required arguments: {params}', ['params' => implode(', ', $missing)]));
}
return $args;
@@ -149,11 +131,12 @@ class Controller extends \yii\base\Controller
/**
* Formats a string with ANSI codes
*
- * You may pass additional parameters using the constants defined in [[yii\helpers\base\Console]].
+ * You may pass additional parameters using the constants defined in [[yii\helpers\Console]].
*
* Example:
+ *
* ~~~
- * $this->ansiFormat('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);
+ * echo $this->ansiFormat('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);
* ~~~
*
* @param string $string the string to be formatted
@@ -161,7 +144,7 @@ class Controller extends \yii\base\Controller
*/
public function ansiFormat($string)
{
- if ($this->getColors()) {
+ if ($this->isColorEnabled()) {
$args = func_get_args();
array_shift($args);
$string = Console::ansiFormat($string, $args);
@@ -173,9 +156,10 @@ class Controller extends \yii\base\Controller
* Prints a string to STDOUT
*
* You may optionally format the string with ANSI codes by
- * passing additional parameters using the constants defined in [[yii\helpers\base\Console]].
+ * passing additional parameters using the constants defined in [[yii\helpers\Console]].
*
* Example:
+ *
* ~~~
* $this->stdout('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);
* ~~~
@@ -185,7 +169,7 @@ class Controller extends \yii\base\Controller
*/
public function stdout($string)
{
- if ($this->getColors()) {
+ if ($this->isColorEnabled()) {
$args = func_get_args();
array_shift($args);
$string = Console::ansiFormat($string, $args);
@@ -197,9 +181,10 @@ class Controller extends \yii\base\Controller
* Prints a string to STDERR
*
* You may optionally format the string with ANSI codes by
- * passing additional parameters using the constants defined in [[yii\helpers\base\Console]].
+ * passing additional parameters using the constants defined in [[yii\helpers\Console]].
*
* Example:
+ *
* ~~~
* $this->stderr('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);
* ~~~
@@ -209,7 +194,7 @@ class Controller extends \yii\base\Controller
*/
public function stderr($string)
{
- if ($this->getColors(STDERR)) {
+ if ($this->isColorEnabled(STDERR)) {
$args = func_get_args();
array_shift($args);
$string = Console::ansiFormat($string, $args);
@@ -222,6 +207,7 @@ class Controller extends \yii\base\Controller
*
* @param string $text prompt string
* @param array $options the options to validate the input:
+ *
* - required: whether it is required or not
* - default: default value if no input is inserted by the user
* - pattern: regular expression pattern to validate user input
@@ -230,7 +216,7 @@ class Controller extends \yii\base\Controller
* - $error: the error value passed by reference if validation failed.
* @return string the user input
*/
- public function prompt($text, $options = array())
+ public function prompt($text, $options = [])
{
if ($this->interactive) {
return Console::prompt($text, $options);
@@ -244,7 +230,8 @@ class Controller extends \yii\base\Controller
*
* @param string $message to echo out before waiting for user input
* @param boolean $default this value is returned if no selection is made.
- * @return boolean whether user confirmed
+ * @return boolean whether user confirmed.
+ * Will return true if [[interactive]] is false.
*/
public function confirm($message, $default = false)
{
@@ -264,7 +251,7 @@ class Controller extends \yii\base\Controller
*
* @return string An option character the user chose
*/
- public function select($prompt, $options = array())
+ public function select($prompt, $options = [])
{
return Console::select($prompt, $options);
}
@@ -282,6 +269,6 @@ class Controller extends \yii\base\Controller
*/
public function globalOptions()
{
- return array('colors', 'interactive');
+ return ['color', 'interactive'];
}
}
diff --git a/framework/yii/console/Request.php b/framework/yii/console/Request.php
index a9a4b03..d4d3af0 100644
--- a/framework/yii/console/Request.php
+++ b/framework/yii/console/Request.php
@@ -8,13 +8,18 @@
namespace yii\console;
/**
+ * The console Request represents the environment information for a console application.
+ *
+ * It is a wrapper for the PHP `$_SERVER` variable which holds information about the
+ * currently running PHP script and the command line arguments given to it.
+ *
+ * @property array $params The command line arguments. It does not include the entry script name.
+ *
* @author Qiang Xue
* @since 2.0
*/
class Request extends \yii\base\Request
{
- const ANONYMOUS_PARAMS = '-args';
-
private $_params;
/**
@@ -28,7 +33,7 @@ class Request extends \yii\base\Request
$this->_params = $_SERVER['argv'];
array_shift($this->_params);
} else {
- $this->_params = array();
+ $this->_params = [];
}
}
return $this->_params;
@@ -57,16 +62,16 @@ class Request extends \yii\base\Request
$route = '';
}
- $params = array(self::ANONYMOUS_PARAMS => array());
+ $params = [];
foreach ($rawParams as $param) {
if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) {
$name = $matches[1];
$params[$name] = isset($matches[3]) ? $matches[3] : true;
} else {
- $params[self::ANONYMOUS_PARAMS][] = $param;
+ $params[] = $param;
}
}
- return array($route, $params);
+ return [$route, $params];
}
}
diff --git a/framework/yii/console/Response.php b/framework/yii/console/Response.php
index 34f105d..f6e6dd0 100644
--- a/framework/yii/console/Response.php
+++ b/framework/yii/console/Response.php
@@ -8,10 +8,11 @@
namespace yii\console;
/**
+ * The console Response represents the result of a console application by holding the [[exitCode]].
+ *
* @author Qiang Xue
* @since 2.0
*/
class Response extends \yii\base\Response
{
-
}
diff --git a/framework/yii/console/controllers/AssetController.php b/framework/yii/console/controllers/AssetController.php
index dcd1667..cea9fed 100644
--- a/framework/yii/console/controllers/AssetController.php
+++ b/framework/yii/console/controllers/AssetController.php
@@ -22,13 +22,14 @@ use yii\console\Controller;
* yii asset /path/to/myapp/config.php /path/to/myapp/config/assets_compressed.php
* 4. Adjust your web application config to use compressed assets.
*
- * Note: in the console environment some path aliases like '@wwwroot' and '@www' may not exist,
+ * Note: in the console environment some path aliases like '@webroot' and '@web' may not exist,
* so corresponding paths inside the configuration should be specified directly.
*
* Note: by default this command relies on an external tools to perform actual files compression,
* check [[jsCompressor]] and [[cssCompressor]] for more details.
*
- * @property array|\yii\web\AssetManager $assetManager asset manager, which will be used for assets processing.
+ * @property \yii\web\AssetManager $assetManager Asset manager instance. Note that the type of this property
+ * differs in getter and setter. See [[getAssetManager()]] and [[setAssetManager()]] for details.
*
* @author Qiang Xue
* @since 2.0
@@ -41,35 +42,25 @@ class AssetController extends Controller
public $defaultAction = 'compress';
/**
* @var array list of asset bundles to be compressed.
- * The keys are the bundle names, and the values are the configuration
- * arrays for creating the [[yii\web\AssetBundle]] objects.
*/
- public $bundles = array();
- /**
- * @var array list of paths to the extensions, which assets should be also compressed.
- * Each path should contain asset manifest file named "assets.php".
- */
- public $extensions = array();
+ public $bundles = [];
/**
* @var array list of asset bundles, which represents output compressed files.
* You can specify the name of the output compressed file using 'css' and 'js' keys:
* For example:
+ *
* ~~~
- * 'all' => array(
- * 'css' => 'all.css',
- * 'js' => 'all.js',
- * 'depends' => array( ... ),
- * )
+ * 'app\config\AllAsset' => [
+ * 'js' => 'js/all-{ts}.js',
+ * 'css' => 'css/all-{ts}.css',
+ * 'depends' => [ ... ],
+ * ]
* ~~~
+ *
* File names can contain placeholder "{ts}", which will be filled by current timestamp, while
* file creation.
*/
- public $targets = array();
- /**
- * @var array|\yii\web\AssetManager [[yii\web\AssetManager]] instance or its array configuration, which will be used
- * for assets processing.
- */
- private $_assetManager = array();
+ public $targets = [];
/**
* @var string|callback JavaScript file compressor.
* If a string, it is treated as shell command template, which should contain
@@ -92,6 +83,12 @@ class AssetController extends Controller
public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}';
/**
+ * @var array|\yii\web\AssetManager [[yii\web\AssetManager]] instance or its array configuration, which will be used
+ * for assets processing.
+ */
+ private $_assetManager = [];
+
+ /**
* Returns the asset manager instance.
* @throws \yii\console\Exception on invalid configuration.
* @return \yii\web\AssetManager asset manager instance.
@@ -137,9 +134,8 @@ class AssetController extends Controller
public function actionCompress($configFile, $bundleFile)
{
$this->loadConfiguration($configFile);
- $bundles = $this->loadBundles($this->bundles, $this->extensions);
+ $bundles = $this->loadBundles($this->bundles);
$targets = $this->loadTargets($this->targets, $bundles);
- $this->publishBundles($bundles, $this->assetManager);
$timestamp = time();
foreach ($targets as $name => $target) {
echo "Creating output bundle '{$name}':\n";
@@ -177,37 +173,20 @@ class AssetController extends Controller
/**
* Creates full list of source asset bundles.
- * @param array[] $bundles list of asset bundle configurations.
- * @param array $extensions list of the extension paths.
+ * @param string[] $bundles list of asset bundle names
* @return \yii\web\AssetBundle[] list of source asset bundles.
*/
- protected function loadBundles($bundles, $extensions)
+ protected function loadBundles($bundles)
{
echo "Collecting source bundles information...\n";
- $assetManager = $this->getAssetManager();
- $result = array();
-
- $assetManager->bundles = $bundles;
- foreach ($assetManager->bundles as $name => $bundle) {
- $result[$name] = $assetManager->getBundle($name);
- }
-
- foreach ($extensions as $path) {
- $manifest = $path . '/assets.php';
- if (!is_file($manifest)) {
- continue;
- }
- $assetManager->bundles = require($manifest);
- foreach ($assetManager->bundles as $name => $bundle) {
- if (!isset($result[$name])) {
- $result[$name] = $assetManager->getBundle($name);
- }
- }
+ $am = $this->getAssetManager();
+ $result = [];
+ foreach ($bundles as $name) {
+ $result[$name] = $am->getBundle($name);
}
-
- foreach ($result as $name => $bundle) {
- $this->loadBundleDependency($name, $bundle, $result);
+ foreach ($result as $bundle) {
+ $this->loadDependency($bundle, $result);
}
return $result;
@@ -215,30 +194,21 @@ class AssetController extends Controller
/**
* Loads asset bundle dependencies recursively.
- * @param string $name bundle name
* @param \yii\web\AssetBundle $bundle bundle instance
* @param array $result already loaded bundles list.
- * @throws \yii\console\Exception on failure.
+ * @throws Exception on failure.
*/
- protected function loadBundleDependency($name, $bundle, &$result)
+ protected function loadDependency($bundle, &$result)
{
- if (!empty($bundle->depends)) {
- $assetManager = $this->getAssetManager();
- foreach ($bundle->depends as $dependencyName) {
- if (!array_key_exists($dependencyName, $result)) {
- $dependencyBundle = $assetManager->getBundle($dependencyName);
- if ($dependencyBundle === null) {
- throw new Exception("Unable to load dependency bundle '{$dependencyName}' for bundle '{$name}'.");
- } else {
- $result[$dependencyName] = false;
- $this->loadBundleDependency($dependencyName, $dependencyBundle, $result);
- $result[$dependencyName] = $dependencyBundle;
- }
- } else {
- if ($result[$dependencyName] === false) {
- throw new Exception("A circular dependency is detected for target '{$dependencyName}'.");
- }
- }
+ $am = $this->getAssetManager();
+ foreach ($bundle->depends as $name) {
+ if (!isset($result[$name])) {
+ $dependencyBundle = $am->getBundle($name);
+ $result[$name] = false;
+ $this->loadDependency($dependencyBundle, $result);
+ $result[$name] = $dependencyBundle;
+ } elseif ($result[$name] === false) {
+ throw new Exception("A circular dependency is detected for bundle '$name'.");
}
}
}
@@ -248,19 +218,19 @@ class AssetController extends Controller
* @param array $targets output asset bundles configuration.
* @param \yii\web\AssetBundle[] $bundles list of source asset bundles.
* @return \yii\web\AssetBundle[] list of output asset bundles.
- * @throws \yii\console\Exception on failure.
+ * @throws Exception on failure.
*/
protected function loadTargets($targets, $bundles)
{
// build the dependency order of bundles
- $registered = array();
+ $registered = [];
foreach ($bundles as $name => $bundle) {
$this->registerBundle($bundles, $name, $registered);
}
$bundleOrders = array_combine(array_keys($registered), range(0, count($bundles) - 1));
// fill up the target which has empty 'depends'.
- $referenced = array();
+ $referenced = [];
foreach ($targets as $name => $target) {
if (empty($target['depends'])) {
if (!isset($all)) {
@@ -298,28 +268,13 @@ class AssetController extends Controller
return $bundleOrders[$a] > $bundleOrders[$b] ? 1 : -1;
}
});
- $target['class'] = 'yii\\web\\AssetBundle';
+ $target['class'] = $name;
$targets[$name] = Yii::createObject($target);
}
return $targets;
}
/**
- * Publishes given asset bundles.
- * @param \yii\web\AssetBundle[] $bundles asset bundles to be published.
- */
- protected function publishBundles($bundles)
- {
- echo "\nPublishing bundles:\n";
- $assetManager = $this->getAssetManager();
- foreach ($bundles as $name => $bundle) {
- $bundle->publish($assetManager);
- echo " '".$name."' published.\n";
- }
- echo "\n";
- }
-
- /**
* Builds output asset bundle.
* @param \yii\web\AssetBundle $target output asset bundle
* @param string $type either 'js' or 'css'.
@@ -329,10 +284,10 @@ class AssetController extends Controller
*/
protected function buildTarget($target, $type, $bundles, $timestamp)
{
- $outputFile = strtr($target->$type, array(
+ $outputFile = strtr($target->$type, [
'{ts}' => $timestamp,
- ));
- $inputFiles = array();
+ ]);
+ $inputFiles = [];
foreach ($target->depends as $name) {
if (isset($bundles[$name])) {
@@ -348,7 +303,7 @@ class AssetController extends Controller
} else {
$this->compressCssFiles($inputFiles, $target->basePath . '/' . $outputFile);
}
- $target->$type = array($outputFile);
+ $target->$type = [$outputFile];
}
/**
@@ -361,7 +316,7 @@ class AssetController extends Controller
{
echo "Creating new bundle configuration...\n";
- $map = array();
+ $map = [];
foreach ($targets as $name => $target) {
foreach ($target->depends as $bundle) {
$map[$bundle] = $name;
@@ -369,7 +324,7 @@ class AssetController extends Controller
}
foreach ($targets as $name => $target) {
- $depends = array();
+ $depends = [];
foreach ($target->depends as $bn) {
foreach ($bundles[$bn]->depends as $bundle) {
$depends[$map[$bundle]] = true;
@@ -381,15 +336,15 @@ class AssetController extends Controller
// detect possible circular dependencies
foreach ($targets as $name => $target) {
- $registered = array();
+ $registered = [];
$this->registerBundle($targets, $name, $registered);
}
foreach ($map as $bundle => $target) {
- $targets[$bundle] = Yii::createObject(array(
+ $targets[$bundle] = Yii::createObject([
'class' => 'yii\\web\\AssetBundle',
- 'depends' => array($target),
- ));
+ 'depends' => [$target],
+ ]);
}
return $targets;
}
@@ -399,7 +354,7 @@ class AssetController extends Controller
* @param \yii\web\AssetBundle[] $bundles asset bundles list.
* @param string $name bundle name.
* @param array $registered stores already registered names.
- * @throws \yii\console\Exception if circular dependency is detected.
+ * @throws Exception if circular dependency is detected.
*/
protected function registerBundle($bundles, $name, &$registered)
{
@@ -424,9 +379,9 @@ class AssetController extends Controller
*/
protected function saveTargets($targets, $bundleFile)
{
- $array = array();
+ $array = [];
foreach ($targets as $name => $target) {
- foreach (array('js', 'css', 'depends', 'basePath', 'baseUrl') as $prop) {
+ foreach (['js', 'css', 'depends', 'basePath', 'baseUrl'] as $prop) {
if (!empty($target->$prop)) {
$array[$name][$prop] = $target->$prop;
}
@@ -464,10 +419,10 @@ EOD;
if (is_string($this->jsCompressor)) {
$tmpFile = $outputFile . '.tmp';
$this->combineJsFiles($inputFiles, $tmpFile);
- echo shell_exec(strtr($this->jsCompressor, array(
+ echo shell_exec(strtr($this->jsCompressor, [
'{from}' => escapeshellarg($tmpFile),
'{to}' => escapeshellarg($outputFile),
- )));
+ ]));
@unlink($tmpFile);
} else {
call_user_func($this->jsCompressor, $this, $inputFiles, $outputFile);
@@ -493,10 +448,10 @@ EOD;
if (is_string($this->cssCompressor)) {
$tmpFile = $outputFile . '.tmp';
$this->combineCssFiles($inputFiles, $tmpFile);
- echo shell_exec(strtr($this->cssCompressor, array(
+ echo shell_exec(strtr($this->cssCompressor, [
'{from}' => escapeshellarg($tmpFile),
'{to}' => escapeshellarg($outputFile),
- )));
+ ]));
@unlink($tmpFile);
} else {
call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile);
@@ -554,7 +509,7 @@ EOD;
*/
protected function adjustCssUrl($cssContent, $inputFilePath, $outputFilePath)
{
- $sharedPathParts = array();
+ $sharedPathParts = [];
$inputFilePathParts = explode('/', $inputFilePath);
$inputFilePathPartsCount = count($inputFilePathParts);
$outputFilePathParts = explode('/', $outputFilePath);
@@ -617,29 +572,30 @@ EOD;
require('path/to/bundles.php'),
- // The list of extensions to compress:
- 'extensions' => require('path/to/namespaces.php'),
+ 'bundles' => [
+ // 'yii\web\YiiAsset',
+ // 'yii\web\JqueryAsset',
+ ],
// Asset bundle for compression output:
- 'targets' => array(
- 'all' => array(
- 'basePath' => __DIR__,
- 'baseUrl' => '/test',
- 'js' => 'all-{ts}.js',
- 'css' => 'all-{ts}.css',
- ),
- ),
+ 'targets' => [
+ 'app\config\AllAsset' => [
+ 'basePath' => 'path/to/web',
+ 'baseUrl' => '',
+ 'js' => 'js/all-{ts}.js',
+ 'css' => 'css/all-{ts}.css',
+ ],
+ ],
// Asset manager configuration:
- 'assetManager' => array(
+ 'assetManager' => [
'basePath' => __DIR__,
- 'baseUrl' => '/test',
- ),
-);
+ 'baseUrl' => '',
+ ],
+];
EOD;
if (file_exists($configFile)) {
if (!$this->confirm("File '{$configFile}' already exists. Do you wish to overwrite it?")) {
diff --git a/framework/yii/console/controllers/CacheController.php b/framework/yii/console/controllers/CacheController.php
index 95765fa..43932d1 100644
--- a/framework/yii/console/controllers/CacheController.php
+++ b/framework/yii/console/controllers/CacheController.php
@@ -25,7 +25,7 @@ class CacheController extends Controller
*/
public function actionIndex()
{
- $caches = array();
+ $caches = [];
$components = Yii::$app->getComponents();
foreach ($components as $name => $component) {
if ($component instanceof Cache) {
@@ -52,7 +52,7 @@ class CacheController extends Controller
*/
public function actionFlush($component = 'cache')
{
- /** @var $cache Cache */
+ /** @var Cache $cache */
$cache = Yii::$app->getComponent($component);
if (!$cache || !$cache instanceof Cache) {
throw new Exception('Application component "'.$component.'" is not defined or not a cache.');
diff --git a/framework/yii/console/controllers/HelpController.php b/framework/yii/console/controllers/HelpController.php
index 9319163..eee1923 100644
--- a/framework/yii/console/controllers/HelpController.php
+++ b/framework/yii/console/controllers/HelpController.php
@@ -12,7 +12,6 @@ use yii\base\Application;
use yii\base\InlineAction;
use yii\console\Controller;
use yii\console\Exception;
-use yii\console\Request;
use yii\helpers\Console;
use yii\helpers\Inflector;
@@ -32,6 +31,8 @@ use yii\helpers\Inflector;
* In the above, if the command name is not provided, all
* available commands will be displayed.
*
+ * @property array $commands All available command names. This property is read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -56,9 +57,9 @@ class HelpController extends Controller
if ($command !== null) {
$result = Yii::$app->createController($command);
if ($result === false) {
- throw new Exception(Yii::t('yii', 'No help for unknown command "{command}".', array(
- '{command}' => $this->ansiFormat($command, Console::FG_YELLOW),
- )));
+ throw new Exception(Yii::t('yii', 'No help for unknown command "{command}".', [
+ 'command' => $this->ansiFormat($command, Console::FG_YELLOW),
+ ]));
}
list($controller, $actionID) = $result;
@@ -113,7 +114,7 @@ class HelpController extends Controller
{
$prefix = $module instanceof Application ? '' : $module->getUniqueID() . '/';
- $commands = array();
+ $commands = [];
foreach (array_keys($module->controllerMap) as $id) {
$commands[] = $prefix . $id;
}
@@ -123,14 +124,14 @@ class HelpController extends Controller
continue;
}
foreach ($this->getModuleCommands($child) as $command) {
- $commands[] = $prefix . $id . '/' . $command;
+ $commands[] = $command;
}
}
$files = scandir($module->getControllerPath());
foreach ($files as $file) {
if (strcmp(substr($file, -14), 'Controller.php') === 0) {
- $commands[] = $prefix . lcfirst(substr(basename($file), 0, -14));
+ $commands[] = $prefix . Inflector::camel2id(substr(basename($file), 0, -14));
}
}
@@ -241,9 +242,9 @@ class HelpController extends Controller
{
$action = $controller->createAction($actionID);
if ($action === null) {
- throw new Exception(Yii::t('yii', 'No help for unknown sub-command "{command}".', array(
- '{command}' => rtrim($controller->getUniqueId() . '/' . $actionID, '/'),
- )));
+ throw new Exception(Yii::t('yii', 'No help for unknown sub-command "{command}".', [
+ 'command' => rtrim($controller->getUniqueId() . '/' . $actionID, '/'),
+ ]));
}
if ($action instanceof InlineAction) {
$method = new \ReflectionMethod($controller, $action->actionMethod);
@@ -265,7 +266,7 @@ class HelpController extends Controller
} else {
echo 'yii ' . $this->ansiFormat($action->getUniqueId(), Console::FG_YELLOW);
}
- list ($required, $optional) = $this->getArgHelps($method, isset($tags['param']) ? $tags['param'] : array());
+ list ($required, $optional) = $this->getArgHelps($method, isset($tags['param']) ? $tags['param'] : []);
foreach ($required as $arg => $description) {
$this->stdout(' <' . $arg . '>', Console::FG_CYAN);
}
@@ -297,10 +298,10 @@ class HelpController extends Controller
protected function getArgHelps($method, $tags)
{
if (is_string($tags)) {
- $tags = array($tags);
+ $tags = [$tags];
}
$params = $method->getParameters();
- $optional = $required = array();
+ $optional = $required = [];
foreach ($params as $i => $param) {
$name = $param->getName();
$tag = isset($tags[$i]) ? $tags[$i] : '';
@@ -318,7 +319,7 @@ class HelpController extends Controller
}
}
- return array($required, $optional);
+ return [$required, $optional];
}
/**
@@ -330,11 +331,11 @@ class HelpController extends Controller
{
$optionNames = $controller->globalOptions();
if (empty($optionNames)) {
- return array();
+ return [];
}
$class = new \ReflectionClass($controller);
- $options = array();
+ $options = [];
foreach ($class->getProperties() as $property) {
$name = $property->getName();
if (!in_array($name, $optionNames, true)) {
@@ -370,7 +371,7 @@ class HelpController extends Controller
*/
protected function parseComment($comment)
{
- $tags = array();
+ $tags = [];
$comment = "@description \n" . strtr(trim(preg_replace('/^\s*\**( |\t)?/m', '', trim($comment, '/'))), "\r", '');
$parts = preg_split('/^\s*@/m', $comment, -1, PREG_SPLIT_NO_EMPTY);
foreach ($parts as $part) {
@@ -381,7 +382,7 @@ class HelpController extends Controller
} elseif (is_array($tags[$name])) {
$tags[$name][] = trim($matches[2]);
} else {
- $tags[$name] = array($tags[$name], trim($matches[2]));
+ $tags[$name] = [$tags[$name], trim($matches[2])];
}
}
}
@@ -406,6 +407,10 @@ class HelpController extends Controller
if ($type === null) {
$type = gettype($defaultValue);
}
+ if (is_bool($defaultValue)) {
+ // show as integer to avoid confusion
+ $defaultValue = (int)$defaultValue;
+ }
$doc = "$type (defaults to " . var_export($defaultValue, true) . ")";
} elseif (trim($type) !== '') {
$doc = $type;
diff --git a/framework/yii/console/controllers/MessageController.php b/framework/yii/console/controllers/MessageController.php
index 44cf1b1..c580b24 100644
--- a/framework/yii/console/controllers/MessageController.php
+++ b/framework/yii/console/controllers/MessageController.php
@@ -8,109 +8,114 @@
namespace yii\console\controllers;
+use Yii;
use yii\console\Controller;
+use yii\console\Exception;
+use yii\helpers\FileHelper;
/**
* This command extracts messages to be translated from source files.
* The extracted messages are saved as PHP message source files
* under the specified directory.
*
+ * Usage:
+ * 1. Create a configuration file using the 'message/config' command:
+ * yii message/config /path/to/myapp/messages/config.php
+ * 2. Edit the created config file, adjusting it for your web application needs.
+ * 3. Run the 'message/extract' extract, using created config:
+ * yii message /path/to/myapp/messages/config.php
+ *
* @author Qiang Xue
* @since 2.0
*/
class MessageController extends Controller
{
/**
- * Searches for messages to be translated in the specified
- * source files and compiles them into PHP arrays as message source.
+ * @var string controller default action ID.
+ */
+ public $defaultAction = 'extract';
+
+
+ /**
+ * Creates a configuration file for the "extract" command.
*
- * @param string $config the path of the configuration file. You can find
- * an example in framework/messages/config.php.
+ * The generated configuration file contains detailed instructions on
+ * how to customize it to fit for your needs. After customization,
+ * you may use this configuration file with the "extract" command.
*
- * The file can be placed anywhere and must be a valid PHP script which
- * returns an array of name-value pairs. Each name-value pair represents
- * a configuration option.
+ * @param string $filePath output file name or alias.
+ * @throws Exception on failure.
+ */
+ public function actionConfig($filePath)
+ {
+ $filePath = Yii::getAlias($filePath);
+ if (file_exists($filePath)) {
+ if (!$this->confirm("File '{$filePath}' already exists. Do you wish to overwrite it?")) {
+ return;
+ }
+ }
+ copy(Yii::getAlias('@yii/views/messageConfig.php'), $filePath);
+ echo "Configuration file template created at '{$filePath}'.\n\n";
+ }
+
+ /**
+ * Extracts messages to be translated from source code.
*
- * The following options are available:
+ * This command will search through source code files and extract
+ * messages that need to be translated in different languages.
*
- * - sourcePath: string, root directory of all source files.
- * - messagePath: string, root directory containing message translations.
- * - languages: array, list of language codes that the extracted messages
- * should be translated to. For example, array('zh_cn', 'en_au').
- * - fileTypes: array, a list of file extensions (e.g. 'php', 'xml').
- * Only the files whose extension name can be found in this list
- * will be processed. If empty, all files will be processed.
- * - exclude: array, a list of directory and file exclusions. Each
- * exclusion can be either a name or a path. If a file or directory name
- * or path matches the exclusion, it will not be copied. For example,
- * an exclusion of '.svn' will exclude all files and directories whose
- * name is '.svn'. And an exclusion of '/a/b' will exclude file or
- * directory 'sourcePath/a/b'.
- * - translator: the name of the function for translating messages.
- * Defaults to 'Yii::t'. This is used as a mark to find messages to be
- * translated.
- * - overwrite: if message file must be overwritten with the merged messages.
- * - removeOld: if message no longer needs translation it will be removed,
- * instead of being enclosed between a pair of '@@' marks.
- * - sort: sort messages by key when merging, regardless of their translation
- * state (new, obsolete, translated.)
+ * @param string $configFile the path or alias of the configuration file.
+ * You may use the "yii message/config" command to generate
+ * this file and then customize it for your needs.
+ * @throws Exception on failure.
*/
- public function actionIndex($config)
+ public function actionExtract($configFile)
{
- if (!is_file($config)) {
- $this->usageError("the configuration file {$config} does not exist.");
+ $configFile = Yii::getAlias($configFile);
+ if (!is_file($configFile)) {
+ throw new Exception("The configuration file does not exist: $configFile");
}
- $config = require_once($config);
-
- $translator='Yii::t';
- extract($config);
+ $config = array_merge([
+ 'translator' => 'Yii::t',
+ 'overwrite' => false,
+ 'removeUnused' => false,
+ 'sort' => false,
+ ], require($configFile));
- if (!isset($sourcePath, $messagePath, $languages)) {
- $this->usageError('The configuration file must specify "sourcePath", "messagePath" and "languages".');
- }
- if (!is_dir($sourcePath)) {
- $this->usageError("The source path $sourcePath is not a valid directory.");
+ if (!isset($config['sourcePath'], $config['messagePath'], $config['languages'])) {
+ throw new Exception('The configuration file must specify "sourcePath", "messagePath" and "languages".');
}
- if (!is_dir($messagePath)) {
- $this->usageError("The message path $messagePath is not a valid directory.");
- }
- if (empty($languages)) {
- $this->usageError("Languages cannot be empty.");
- }
-
- if (!isset($overwrite)) {
- $overwrite = false;
+ if (!is_dir($config['sourcePath'])) {
+ throw new Exception("The source path {$config['sourcePath']} is not a valid directory.");
}
- if (!isset($removeOld)) {
- $removeOld = false;
+ if (!is_dir($config['messagePath'])) {
+ throw new Exception("The message path {$config['messagePath']} is not a valid directory.");
}
- if (!isset($sort)) {
- $sort = false;
+ if (empty($config['languages'])) {
+ throw new Exception("Languages cannot be empty.");
}
- $options = array();
- if (isset($fileTypes)) {
- $options['fileTypes'] = $fileTypes;
- }
- if (isset($exclude)) {
- $options['exclude'] = $exclude;
- }
- $files = CFileHelper::findFiles(realpath($sourcePath), $options);
+ $files = FileHelper::findFiles(realpath($config['sourcePath']), $config);
- $messages = array();
+ $messages = [];
foreach ($files as $file) {
- $messages = array_merge_recursive($messages, $this->extractMessages($file, $translator));
+ $messages = array_merge_recursive($messages, $this->extractMessages($file, $config['translator']));
}
- foreach ($languages as $language) {
- $dir = $messagePath . DIRECTORY_SEPARATOR . $language;
+ foreach ($config['languages'] as $language) {
+ $dir = $config['messagePath'] . DIRECTORY_SEPARATOR . $language;
if (!is_dir($dir)) {
@mkdir($dir);
}
foreach ($messages as $category => $msgs) {
+ $file = str_replace("\\", '/', "$dir/$category.php");
+ $path = dirname($file);
+ if (!is_dir($path)) {
+ mkdir($path, 0755, true);
+ }
$msgs = array_values(array_unique($msgs));
- $this->generateMessageFile($msgs, $dir . DIRECTORY_SEPARATOR . $category . '.php', $overwrite, $removeOld, $sort);
+ $this->generateMessageFile($msgs, $file, $config['overwrite'], $config['removeUnused'], $config['sort']);
}
}
}
@@ -126,18 +131,23 @@ class MessageController extends Controller
{
echo "Extracting messages from $fileName...\n";
$subject = file_get_contents($fileName);
- $n = preg_match_all(
- '/\b' . $translator . '\s*\(\s*(\'.*?(? 0) {
$merged[$message] = $translated[$message];
} else {
$untranslated[] = $message;
@@ -173,15 +183,15 @@ class MessageController extends Controller
}
ksort($merged);
sort($untranslated);
- $todo = array();
+ $todo = [];
foreach ($untranslated as $message) {
$todo[$message] = '';
}
ksort($translated);
foreach ($translated as $message => $translation) {
- if (!isset($merged[$message]) && !isset($todo[$message]) && !$removeOld) {
+ if (!isset($merged[$message]) && !isset($todo[$message]) && !$removeUnused) {
if (substr($translation, 0, 2) === '@@' && substr($translation, -2) === '@@') {
- $todo[$message]=$translation;
+ $todo[$message] = $translation;
} else {
$todo[$message] = '@@' . $translation . '@@';
}
@@ -196,7 +206,7 @@ class MessageController extends Controller
}
echo "translation merged.\n";
} else {
- $merged = array();
+ $merged = [];
foreach ($messages as $message) {
$merged[$message] = '';
}
@@ -209,7 +219,7 @@ class MessageController extends Controller
/**
* Message translations.
*
- * This file is automatically generated by 'yii message' command.
+ * This file is automatically generated by 'yii {$this->id}' command.
* It contains the localizable messages extracted from source code.
* You may modify this file by translating the extracted messages.
*
@@ -221,7 +231,7 @@ class MessageController extends Controller
* Message string can be used with plural forms format. Check i18n section
* of the guide for details.
*
- * NOTE, this file must be saved in UTF-8 encoding.
+ * NOTE: this file must be saved in UTF-8 encoding.
*/
return $array;
diff --git a/framework/yii/console/controllers/MigrateController.php b/framework/yii/console/controllers/MigrateController.php
index eca7787..5651b9c 100644
--- a/framework/yii/console/controllers/MigrateController.php
+++ b/framework/yii/console/controllers/MigrateController.php
@@ -33,7 +33,7 @@ use yii\helpers\ArrayHelper;
*
* ~~~
* CREATE TABLE tbl_migration (
- * version varchar(255) PRIMARY KEY,
+ * version varchar(180) PRIMARY KEY,
* apply_time integer
* )
* ~~~
@@ -96,7 +96,9 @@ class MigrateController extends Controller
*/
public function globalOptions()
{
- return array('migrationPath', 'migrationTable', 'db', 'templateFile', 'interactive');
+ return array_merge(parent::globalOptions(), [
+ 'migrationPath', 'migrationTable', 'db', 'templateFile', 'interactive', 'color'
+ ]);
}
/**
@@ -149,7 +151,7 @@ class MigrateController extends Controller
$migrations = $this->getNewMigrations();
if (empty($migrations)) {
echo "No new migration found. Your system is up-to-date.\n";
- Yii::$app->end();
+ return;
}
$total = count($migrations);
@@ -358,10 +360,10 @@ class MigrateController extends Controller
if ($this->confirm("Set migration history at $originalVersion?")) {
$command = $this->db->createCommand();
for ($j = 0; $j <= $i; ++$j) {
- $command->insert($this->migrationTable, array(
+ $command->insert($this->migrationTable, [
'version' => $migrations[$j],
'apply_time' => time(),
- ))->execute();
+ ])->execute();
}
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
}
@@ -379,9 +381,9 @@ class MigrateController extends Controller
if ($this->confirm("Set migration history at $originalVersion?")) {
$command = $this->db->createCommand();
for ($j = 0; $j < $i; ++$j) {
- $command->delete($this->migrationTable, array(
+ $command->delete($this->migrationTable, [
'version' => $migrations[$j],
- ))->execute();
+ ])->execute();
}
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
}
@@ -488,9 +490,7 @@ class MigrateController extends Controller
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $name . '.php';
if ($this->confirm("Create new migration '$file'?")) {
- $content = $this->renderFile(Yii::getAlias($this->templateFile), array(
- 'className' => $name,
- ));
+ $content = $this->renderFile(Yii::getAlias($this->templateFile), ['className' => $name]);
file_put_contents($file, $content);
echo "New migration created successfully.\n";
}
@@ -511,10 +511,10 @@ class MigrateController extends Controller
$start = microtime(true);
$migration = $this->createMigration($class);
if ($migration->up() !== false) {
- $this->db->createCommand()->insert($this->migrationTable, array(
+ $this->db->createCommand()->insert($this->migrationTable, [
'version' => $class,
'apply_time' => time(),
- ))->execute();
+ ])->execute();
$time = microtime(true) - $start;
echo "*** applied $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
return true;
@@ -540,9 +540,9 @@ class MigrateController extends Controller
$start = microtime(true);
$migration = $this->createMigration($class);
if ($migration->down() !== false) {
- $this->db->createCommand()->delete($this->migrationTable, array(
+ $this->db->createCommand()->delete($this->migrationTable, [
'version' => $class,
- ))->execute();
+ ])->execute();
$time = microtime(true) - $start;
echo "*** reverted $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
return true;
@@ -562,9 +562,7 @@ class MigrateController extends Controller
{
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
require_once($file);
- return new $class(array(
- 'db' => $this->db,
- ));
+ return new $class(['db' => $this->db]);
}
/**
@@ -578,11 +576,11 @@ class MigrateController extends Controller
$this->createMigrationHistoryTable();
}
$query = new Query;
- $rows = $query->select(array('version', 'apply_time'))
+ $rows = $query->select(['version', 'apply_time'])
->from($this->migrationTable)
->orderBy('version DESC')
->limit($limit)
- ->createCommand()
+ ->createCommand($this->db)
->queryAll();
$history = ArrayHelper::map($rows, 'version', 'apply_time');
unset($history[self::BASE_MIGRATION]);
@@ -595,14 +593,14 @@ class MigrateController extends Controller
protected function createMigrationHistoryTable()
{
echo 'Creating migration history table "' . $this->migrationTable . '"...';
- $this->db->createCommand()->createTable($this->migrationTable, array(
- 'version' => 'varchar(255) NOT NULL PRIMARY KEY',
+ $this->db->createCommand()->createTable($this->migrationTable, [
+ 'version' => 'varchar(180) NOT NULL PRIMARY KEY',
'apply_time' => 'integer',
- ))->execute();
- $this->db->createCommand()->insert($this->migrationTable, array(
+ ])->execute();
+ $this->db->createCommand()->insert($this->migrationTable, [
'version' => self::BASE_MIGRATION,
'apply_time' => time(),
- ))->execute();
+ ])->execute();
echo "done.\n";
}
@@ -612,12 +610,12 @@ class MigrateController extends Controller
*/
protected function getNewMigrations()
{
- $applied = array();
+ $applied = [];
foreach ($this->getMigrationHistory(-1) as $version => $time) {
$applied[substr($version, 1, 13)] = true;
}
- $migrations = array();
+ $migrations = [];
$handle = opendir($this->migrationPath);
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') {
diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php
new file mode 100644
index 0000000..2292286
--- /dev/null
+++ b/framework/yii/data/ActiveDataProvider.php
@@ -0,0 +1,182 @@
+ Post::find(),
+ * 'pagination' => [
+ * 'pageSize' => 20,
+ * ],
+ * ]);
+ *
+ * // get the posts in the current page
+ * $posts = $provider->getModels();
+ * ~~~
+ *
+ * And the following example shows how to use ActiveDataProvider without ActiveRecord:
+ *
+ * ~~~
+ * $query = new Query;
+ * $provider = new ActiveDataProvider([
+ * 'query' => $query->from('tbl_post'),
+ * 'pagination' => [
+ * 'pageSize' => 20,
+ * ],
+ * ]);
+ *
+ * // get the posts in the current page
+ * $posts = $provider->getModels();
+ * ~~~
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class ActiveDataProvider extends BaseDataProvider
+{
+ /**
+ * @var QueryInterface the query that is used to fetch data models and [[totalCount]]
+ * if it is not explicitly set.
+ */
+ public $query;
+ /**
+ * @var string|callable the column that is used as the key of the data models.
+ * This can be either a column name, or a callable that returns the key value of a given data model.
+ *
+ * If this is not set, the following rules will be used to determine the keys of the data models:
+ *
+ * - If [[query]] is an [[ActiveQuery]] instance, the primary keys of [[ActiveQuery::modelClass]] will be used.
+ * - Otherwise, the keys of the [[models]] array will be used.
+ *
+ * @see getKeys()
+ */
+ public $key;
+ /**
+ * @var Connection|string the DB connection object or the application component ID of the DB connection.
+ * If not set, the default DB connection will be used.
+ */
+ public $db;
+
+ /**
+ * Initializes the DB connection component.
+ * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
+ * @throws InvalidConfigException if [[db]] is invalid.
+ */
+ public function init()
+ {
+ parent::init();
+ if (is_string($this->db)) {
+ $this->db = Yii::$app->getComponent($this->db);
+ if ($this->db === null) {
+ throw new InvalidConfigException('The "db" property must be a valid DB Connection application component.');
+ }
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function prepareModels()
+ {
+ if (!$this->query instanceof QueryInterface) {
+ throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
+ }
+ if (($pagination = $this->getPagination()) !== false) {
+ $pagination->totalCount = $this->getTotalCount();
+ $this->query->limit($pagination->getLimit())->offset($pagination->getOffset());
+ }
+ if (($sort = $this->getSort()) !== false) {
+ $this->query->addOrderBy($sort->getOrders());
+ }
+ return $this->query->all($this->db);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function prepareKeys($models)
+ {
+ $keys = [];
+ if ($this->key !== null) {
+ foreach ($models as $model) {
+ if (is_string($this->key)) {
+ $keys[] = $model[$this->key];
+ } else {
+ $keys[] = call_user_func($this->key, $model);
+ }
+ }
+ return $keys;
+ } elseif ($this->query instanceof ActiveQueryInterface) {
+ /** @var \yii\db\ActiveRecord $class */
+ $class = $this->query->modelClass;
+ $pks = $class::primaryKey();
+ if (count($pks) === 1) {
+ $pk = $pks[0];
+ foreach ($models as $model) {
+ $keys[] = $model[$pk];
+ }
+ } else {
+ foreach ($models as $model) {
+ $kk = [];
+ foreach ($pks as $pk) {
+ $kk[$pk] = $model[$pk];
+ }
+ $keys[] = $kk;
+ }
+ }
+ return $keys;
+ } else {
+ return array_keys($models);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function prepareTotalCount()
+ {
+ if (!$this->query instanceof QueryInterface) {
+ throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
+ }
+ $query = clone $this->query;
+ return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setSort($value)
+ {
+ parent::setSort($value);
+ if (($sort = $this->getSort()) !== false && empty($sort->attributes) && $this->query instanceof ActiveQueryInterface) {
+ /** @var Model $model */
+ $model = new $this->query->modelClass;
+ foreach ($model->attributes() as $attribute) {
+ $sort->attributes[$attribute] = [
+ 'asc' => [$attribute => SORT_ASC],
+ 'desc' => [$attribute => SORT_DESC],
+ 'label' => $model->getAttributeLabel($attribute),
+ ];
+ }
+ }
+ }
+}
diff --git a/framework/yii/data/ArrayDataProvider.php b/framework/yii/data/ArrayDataProvider.php
new file mode 100644
index 0000000..2b694c7
--- /dev/null
+++ b/framework/yii/data/ArrayDataProvider.php
@@ -0,0 +1,131 @@
+ $query->from('tbl_post')->all(),
+ * 'sort' => [
+ * 'attributes' => ['id', 'username', 'email'],
+ * ],
+ * 'pagination' => [
+ * 'pageSize' => 10,
+ * ],
+ * ]);
+ * // get the posts in the current page
+ * $posts = $provider->getModels();
+ * ~~~
+ *
+ * Note: if you want to use the sorting feature, you must configure the [[sort]] property
+ * so that the provider knows which columns can be sorted.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class ArrayDataProvider extends BaseDataProvider
+{
+ /**
+ * @var string|callable the column that is used as the key of the data models.
+ * This can be either a column name, or a callable that returns the key value of a given data model.
+ * If this is not set, the index of the [[models]] array will be used.
+ * @see getKeys()
+ */
+ public $key;
+ /**
+ * @var array the data that is not paginated or sorted. When pagination is enabled,
+ * this property usually contains more elements than [[models]].
+ * The array elements must use zero-based integer keys.
+ */
+ public $allModels;
+
+
+ /**
+ * @inheritdoc
+ */
+ protected function prepareModels()
+ {
+ if (($models = $this->allModels) === null) {
+ return [];
+ }
+
+ if (($sort = $this->getSort()) !== false) {
+ $models = $this->sortModels($models, $sort);
+ }
+
+ if (($pagination = $this->getPagination()) !== false) {
+ $pagination->totalCount = $this->getTotalCount();
+ $models = array_slice($models, $pagination->getOffset(), $pagination->getLimit());
+ }
+
+ return $models;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function prepareKeys($models)
+ {
+ if ($this->key !== null) {
+ $keys = [];
+ foreach ($models as $model) {
+ if (is_string($this->key)) {
+ $keys[] = $model[$this->key];
+ } else {
+ $keys[] = call_user_func($this->key, $model);
+ }
+ }
+ return $keys;
+ } else {
+ return array_keys($models);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function prepareTotalCount()
+ {
+ return count($this->allModels);
+ }
+
+ /**
+ * Sorts the data models according to the given sort definition
+ * @param array $models the models to be sorted
+ * @param Sort $sort the sort definition
+ * @return array the sorted data models
+ */
+ protected function sortModels($models, $sort)
+ {
+ $orders = $sort->getOrders();
+ if (!empty($orders)) {
+ ArrayHelper::multisort($models, array_keys($orders), array_values($orders));
+ }
+ return $models;
+ }
+}
diff --git a/framework/yii/data/BaseDataProvider.php b/framework/yii/data/BaseDataProvider.php
new file mode 100644
index 0000000..cf094c7
--- /dev/null
+++ b/framework/yii/data/BaseDataProvider.php
@@ -0,0 +1,249 @@
+
+ * @since 2.0
+ */
+abstract class BaseDataProvider extends Component implements DataProviderInterface
+{
+ /**
+ * @var string an ID that uniquely identifies the data provider among all data providers.
+ * You should set this property if the same page contains two or more different data providers.
+ * Otherwise, the [[pagination]] and [[sort]] mainly not work properly.
+ */
+ public $id;
+
+ private $_sort;
+ private $_pagination;
+ private $_keys;
+ private $_models;
+ private $_totalCount;
+
+
+ /**
+ * Prepares the data models that will be made available in the current page.
+ * @return array the available data models
+ */
+ abstract protected function prepareModels();
+
+ /**
+ * Prepares the keys associated with the currently available data models.
+ * @param array $models the available data models
+ * @return array the keys
+ */
+ abstract protected function prepareKeys($models);
+
+ /**
+ * Returns a value indicating the total number of data models in this data provider.
+ * @return integer total number of data models in this data provider.
+ */
+ abstract protected function prepareTotalCount();
+
+ /**
+ * Prepares the data models and keys.
+ *
+ * This method will prepare the data models and keys that can be retrieved via
+ * [[getModels()]] and [[getKeys()]].
+ *
+ * This method will be implicitly called by [[getModels()]] and [[getKeys()]] if it has not been called before.
+ *
+ * @param boolean $forcePrepare whether to force data preparation even if it has been done before.
+ */
+ public function prepare($forcePrepare = false)
+ {
+ if ($forcePrepare || $this->_models === null) {
+ $this->_models = $this->prepareModels();
+ }
+ if ($forcePrepare || $this->_keys === null) {
+ $this->_keys = $this->prepareKeys($this->_models);
+ }
+ }
+
+ /**
+ * Returns the data models in the current page.
+ * @return array the list of data models in the current page.
+ */
+ public function getModels()
+ {
+ $this->prepare();
+ return $this->_models;
+ }
+
+ /**
+ * Sets the data models in the current page.
+ * @param array $models the models in the current page
+ */
+ public function setModels($models)
+ {
+ $this->_models = $models;
+ }
+
+ /**
+ * Returns the key values associated with the data models.
+ * @return array the list of key values corresponding to [[models]]. Each data model in [[models]]
+ * is uniquely identified by the corresponding key value in this array.
+ */
+ public function getKeys()
+ {
+ $this->prepare();
+ return $this->_keys;
+ }
+
+ /**
+ * Sets the key values associated with the data models.
+ * @param array $keys the list of key values corresponding to [[models]].
+ */
+ public function setKeys($keys)
+ {
+ $this->_keys = $keys;
+ }
+
+ /**
+ * Returns the number of data models in the current page.
+ * @return integer the number of data models in the current page.
+ */
+ public function getCount()
+ {
+ return count($this->getModels());
+ }
+
+ /**
+ * Returns the total number of data models.
+ * When [[pagination]] is false, this returns the same value as [[count]].
+ * Otherwise, it will call [[prepareTotalCount()]] to get the count.
+ * @return integer total number of possible data models.
+ */
+ public function getTotalCount()
+ {
+ if ($this->getPagination() === false) {
+ return $this->getCount();
+ } elseif ($this->_totalCount === null) {
+ $this->_totalCount = $this->prepareTotalCount();
+ }
+ return $this->_totalCount;
+ }
+
+ /**
+ * Sets the total number of data models.
+ * @param integer $value the total number of data models.
+ */
+ public function setTotalCount($value)
+ {
+ $this->_totalCount = $value;
+ }
+
+ /**
+ * Returns the pagination object used by this data provider.
+ * Note that you should call [[prepare()]] or [[getModels()]] first to get correct values
+ * of [[Pagination::totalCount]] and [[Pagination::pageCount]].
+ * @return Pagination|boolean the pagination object. If this is false, it means the pagination is disabled.
+ */
+ public function getPagination()
+ {
+ if ($this->_pagination === null) {
+ $this->setPagination([]);
+ }
+ return $this->_pagination;
+ }
+
+ /**
+ * Sets the pagination for this data provider.
+ * @param array|Pagination|boolean $value the pagination to be used by this data provider.
+ * This can be one of the following:
+ *
+ * - a configuration array for creating the pagination object. The "class" element defaults
+ * to 'yii\data\Pagination'
+ * - an instance of [[Pagination]] or its subclass
+ * - false, if pagination needs to be disabled.
+ *
+ * @throws InvalidParamException
+ */
+ public function setPagination($value)
+ {
+ if (is_array($value)) {
+ $config = ['class' => Pagination::className()];
+ if ($this->id !== null) {
+ $config['pageVar'] = $this->id . '-page';
+ }
+ $this->_pagination = Yii::createObject(array_merge($config, $value));
+ } elseif ($value instanceof Pagination || $value === false) {
+ $this->_pagination = $value;
+ } else {
+ throw new InvalidParamException('Only Pagination instance, configuration array or false is allowed.');
+ }
+ }
+
+ /**
+ * @return Sort|boolean the sorting object. If this is false, it means the sorting is disabled.
+ */
+ public function getSort()
+ {
+ if ($this->_sort === null) {
+ $this->setSort([]);
+ }
+ return $this->_sort;
+ }
+
+ /**
+ * Sets the sort definition for this data provider.
+ * @param array|Sort|boolean $value the sort definition to be used by this data provider.
+ * This can be one of the following:
+ *
+ * - a configuration array for creating the sort definition object. The "class" element defaults
+ * to 'yii\data\Sort'
+ * - an instance of [[Sort]] or its subclass
+ * - false, if sorting needs to be disabled.
+ *
+ * @throws InvalidParamException
+ */
+ public function setSort($value)
+ {
+ if (is_array($value)) {
+ $config = ['class' => Sort::className()];
+ if ($this->id !== null) {
+ $config['sortVar'] = $this->id . '-sort';
+ }
+ $this->_sort = Yii::createObject(array_merge($config, $value));
+ } elseif ($value instanceof Sort || $value === false) {
+ $this->_sort = $value;
+ } else {
+ throw new InvalidParamException('Only Sort instance, configuration array or false is allowed.');
+ }
+ }
+
+ /**
+ * Refreshes the data provider.
+ * After calling this method, if [[getModels()]], [[getKeys()]] or [[getTotalCount()]] is called again,
+ * they will re-execute the query and return the latest data available.
+ */
+ public function refresh()
+ {
+ $this->_totalCount = null;
+ $this->_models = null;
+ $this->_keys = null;
+ }
+}
diff --git a/framework/yii/data/DataProviderInterface.php b/framework/yii/data/DataProviderInterface.php
new file mode 100644
index 0000000..1dea1e6
--- /dev/null
+++ b/framework/yii/data/DataProviderInterface.php
@@ -0,0 +1,70 @@
+
+ * @since 2.0
+ */
+interface DataProviderInterface
+{
+ /**
+ * Prepares the data models and keys.
+ *
+ * This method will prepare the data models and keys that can be retrieved via
+ * [[getModels()]] and [[getKeys()]].
+ *
+ * This method will be implicitly called by [[getModels()]] and [[getKeys()]] if it has not been called before.
+ *
+ * @param boolean $forcePrepare whether to force data preparation even if it has been done before.
+ */
+ public function prepare($forcePrepare = false);
+
+ /**
+ * Returns the number of data models in the current page.
+ * This is equivalent to `count($provider->getModels())`.
+ * When [[pagination]] is false, this is the same as [[totalCount]].
+ * @return integer the number of data models in the current page.
+ */
+ public function getCount();
+
+ /**
+ * Returns the total number of data models.
+ * When [[pagination]] is false, this is the same as [[count]].
+ * @return integer total number of possible data models.
+ */
+ public function getTotalCount();
+
+ /**
+ * Returns the data models in the current page.
+ * @return array the list of data models in the current page.
+ */
+ public function getModels();
+
+ /**
+ * Returns the key values associated with the data models.
+ * @return array the list of key values corresponding to [[models]]. Each data model in [[models]]
+ * is uniquely identified by the corresponding key value in this array.
+ */
+ public function getKeys();
+
+ /**
+ * @return Sort the sorting object. If this is false, it means the sorting is disabled.
+ */
+ public function getSort();
+
+ /**
+ * @return Pagination the pagination object. If this is false, it means the pagination is disabled.
+ */
+ public function getPagination();
+}
diff --git a/framework/yii/data/Pagination.php b/framework/yii/data/Pagination.php
index 4e25809..8fa8b87 100644
--- a/framework/yii/data/Pagination.php
+++ b/framework/yii/data/Pagination.php
@@ -9,12 +9,13 @@ namespace yii\data;
use Yii;
use yii\base\Object;
+use yii\web\Request;
/**
* Pagination represents information relevant to pagination of data items.
*
* When data needs to be rendered in multiple pages, Pagination can be used to
- * represent information such as [[itemCount|total item count]], [[pageSize|page size]],
+ * represent information such as [[totalCount|total item count]], [[pageSize|page size]],
* [[page|current page]], etc. These information can be passed to [[yii\widgets\Pager|pagers]]
* to render pagination buttons or links.
*
@@ -26,17 +27,17 @@ use yii\base\Object;
* ~~~
* function actionIndex()
* {
- * $query = Article::find()->where(array('status' => 1));
+ * $query = Article::find()->where(['status' => 1]);
* $countQuery = clone $query;
- * $pages = new Pagination($countQuery->count());
+ * $pages = new Pagination(['totalCount' => $countQuery->count()]);
* $models = $query->offset($pages->offset)
* ->limit($pages->limit)
* ->all();
*
- * $this->render('index', array(
+ * return $this->render('index', [
* 'models' => $models,
* 'pages' => $pages,
- * ));
+ * ]);
* }
* ~~~
*
@@ -48,17 +49,18 @@ use yii\base\Object;
* }
*
* // display pagination
- * LinkPager::widget(array(
+ * echo LinkPager::widget([
* 'pagination' => $pages,
- * ));
+ * ]);
* ~~~
*
- * @property integer $pageCount Number of pages.
- * @property integer $page The zero-based index of the current page.
- * @property integer $offset The offset of the data. This may be used to set the
- * OFFSET value for a SQL statement for fetching the current page of data.
- * @property integer $limit The limit of the data. This may be used to set the
- * LIMIT value for a SQL statement for fetching the current page of data.
+ * @property integer $limit The limit of the data. This may be used to set the LIMIT value for a SQL statement
+ * for fetching the current page of data. Note that if the page size is infinite, a value -1 will be returned.
+ * This property is read-only.
+ * @property integer $offset The offset of the data. This may be used to set the OFFSET value for a SQL
+ * statement for fetching the current page of data. This property is read-only.
+ * @property integer $page The zero-based current page number.
+ * @property integer $pageCount Number of pages. This property is read-only.
*
* @author Qiang Xue
* @since 2.0
@@ -82,40 +84,35 @@ class Pagination extends Object
public $route;
/**
* @var array parameters (name => value) that should be used to obtain the current page number
- * and to create new pagination URLs. If not set, $_GET will be used instead.
+ * and to create new pagination URLs. If not set, all parameters from $_GET will be used instead.
*
* The array element indexed by [[pageVar]] is considered to be the current page number.
* If the element does not exist, the current page number is considered 0.
*/
public $params;
/**
+ * @var \yii\web\UrlManager the URL manager used for creating pagination URLs. If not set,
+ * the "urlManager" application component will be used.
+ */
+ public $urlManager;
+ /**
* @var boolean whether to check if [[page]] is within valid range.
* When this property is true, the value of [[page]] will always be between 0 and ([[pageCount]]-1).
- * Because [[pageCount]] relies on the correct value of [[itemCount]] which may not be available
+ * Because [[pageCount]] relies on the correct value of [[totalCount]] which may not be available
* in some cases (e.g. MongoDB), you may want to set this property to be false to disable the page
* number validation. By doing so, [[page]] will return the value indexed by [[pageVar]] in [[params]].
*/
public $validatePage = true;
/**
- * @var integer number of items on each page. Defaults to 10.
+ * @var integer number of items on each page. Defaults to 20.
* If it is less than 1, it means the page size is infinite, and thus a single page contains all items.
*/
- public $pageSize = 10;
+ public $pageSize = 20;
/**
* @var integer total number of items.
*/
- public $itemCount;
+ public $totalCount = 0;
- /**
- * Constructor.
- * @param integer $itemCount total number of items.
- * @param array $config name-value pairs that will be used to initialize the object properties
- */
- public function __construct($itemCount, $config = array())
- {
- $this->itemCount = $itemCount;
- parent::__construct($config);
- }
/**
* @return integer number of pages
@@ -123,10 +120,10 @@ class Pagination extends Object
public function getPageCount()
{
if ($this->pageSize < 1) {
- return $this->itemCount > 0 ? 1 : 0;
+ return $this->totalCount > 0 ? 1 : 0;
} else {
- $itemCount = $this->itemCount < 0 ? 0 : (int)$this->itemCount;
- return (int)(($itemCount + $this->pageSize - 1) / $this->pageSize);
+ $totalCount = $this->totalCount < 0 ? 0 : (int)$this->totalCount;
+ return (int)(($totalCount + $this->pageSize - 1) / $this->pageSize);
}
}
@@ -140,7 +137,10 @@ class Pagination extends Object
public function getPage($recalculate = false)
{
if ($this->_page === null || $recalculate) {
- $params = $this->params === null ? $_GET : $this->params;
+ if (($params = $this->params) === null) {
+ $request = Yii::$app->getRequest();
+ $params = $request instanceof Request ? $request->get() : [];
+ }
if (isset($params[$this->pageVar]) && is_scalar($params[$this->pageVar])) {
$this->_page = (int)$params[$this->pageVar] - 1;
if ($this->validatePage) {
@@ -172,20 +172,29 @@ class Pagination extends Object
* Creates the URL suitable for pagination with the specified page number.
* This method is mainly called by pagers when creating URLs used to perform pagination.
* @param integer $page the zero-based page number that the URL should point to.
+ * @param boolean $absolute whether to create an absolute URL. Defaults to `false`.
* @return string the created URL
* @see params
* @see forcePageVar
*/
- public function createUrl($page)
+ public function createUrl($page, $absolute = false)
{
- $params = $this->params === null ? $_GET : $this->params;
+ if (($params = $this->params) === null) {
+ $request = Yii::$app->getRequest();
+ $params = $request instanceof Request ? $request->get() : [];
+ }
if ($page > 0 || $page >= 0 && $this->forcePageVar) {
$params[$this->pageVar] = $page + 1;
} else {
unset($params[$this->pageVar]);
}
- $route = $this->route === null ? Yii::$app->controller->route : $this->route;
- return Yii::$app->getUrlManager()->createUrl($route, $params);
+ $route = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
+ $urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;
+ if ($absolute) {
+ return $urlManager->createAbsoluteUrl($route, $params);
+ } else {
+ return $urlManager->createUrl($route, $params);
+ }
}
/**
diff --git a/framework/yii/data/Sort.php b/framework/yii/data/Sort.php
index a46d0ce..0432006 100644
--- a/framework/yii/data/Sort.php
+++ b/framework/yii/data/Sort.php
@@ -8,8 +8,11 @@
namespace yii\data;
use Yii;
+use yii\base\InvalidConfigException;
use yii\base\Object;
use yii\helpers\Html;
+use yii\helpers\Inflector;
+use yii\web\Request;
/**
* Sort represents information relevant to sorting.
@@ -23,25 +26,27 @@ use yii\helpers\Html;
* ~~~
* function actionIndex()
* {
- * $sort = new Sort(array(
- * 'attributes' => array(
+ * $sort = new Sort([
+ * 'attributes' => [
* 'age',
- * 'name' => array(
- * 'asc' => array('last_name', 'first_name'),
- * 'desc' => array('last_name' => true, 'first_name' => true),
- * ),
- * ),
- * ));
+ * 'name' => [
+ * 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
+ * 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
+ * 'default' => SORT_DESC,
+ * 'label' => 'Name',
+ * ],
+ * ],
+ * ]);
*
* $models = Article::find()
- * ->where(array('status' => 1))
+ * ->where(['status' => 1])
* ->orderBy($sort->orders)
* ->all();
*
- * $this->render('index', array(
+ * return $this->render('index', [
* 'models' => $models,
* 'sort' => $sort,
- * ));
+ * ]);
* }
* ~~~
*
@@ -49,7 +54,7 @@ use yii\helpers\Html;
*
* ~~~
* // display links leading to sort actions
- * echo $sort->link('name', 'Name') . ' | ' . $sort->link('age', 'Age');
+ * echo $sort->link('name') . ' | ' . $sort->link('age');
*
* foreach ($models as $model) {
* // display $model here
@@ -61,10 +66,10 @@ use yii\helpers\Html;
* sorted by the orders specified by the Sort object. In the view, we show two hyperlinks
* that can lead to pages with the data sorted by the corresponding attributes.
*
- * @property array $orders Sort directions indexed by column names. The sort direction
- * can be either [[Sort::ASC]] for ascending order or [[Sort::DESC]] for descending order.
- * @property array $attributeOrders Sort directions indexed by attribute names. The sort
- * direction can be either [[Sort::ASC]] for ascending order or [[Sort::DESC]] for descending order.
+ * @property array $attributeOrders Sort directions indexed by attribute names. Sort direction can be either
+ * `SORT_ASC` for ascending order or `SORT_DESC` for descending order. This property is read-only.
+ * @property array $orders The columns (keys) and their corresponding sort directions (values). This can be
+ * passed to [[\yii\db\Query::orderBy()]] to construct a DB query. This property is read-only.
*
* @author Qiang Xue
* @since 2.0
@@ -72,16 +77,6 @@ use yii\helpers\Html;
class Sort extends Object
{
/**
- * Sort ascending
- */
- const ASC = false;
-
- /**
- * Sort descending
- */
- const DESC = true;
-
- /**
* @var boolean whether the sorting can be applied to multiple attributes simultaneously.
* Defaults to false, which means each time the data can only be sorted by one attribute.
*/
@@ -92,24 +87,27 @@ class Sort extends Object
* described using the following example:
*
* ~~~
- * array(
+ * [
* 'age',
- * 'user' => array(
- * 'asc' => array('first_name' => Sort::ASC, 'last_name' => Sort::ASC),
- * 'desc' => array('first_name' => Sort::DESC, 'last_name' => Sort::DESC),
- * 'default' => 'desc',
- * ),
- * )
+ * 'name' => [
+ * 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
+ * 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
+ * 'default' => SORT_DESC,
+ * 'label' => 'Name',
+ * ],
+ * ]
* ~~~
*
* In the above, two attributes are declared: "age" and "user". The "age" attribute is
* a simple attribute which is equivalent to the following:
*
* ~~~
- * 'age' => array(
- * 'asc' => array('age' => Sort::ASC),
- * 'desc' => array('age' => Sort::DESC),
- * )
+ * 'age' => [
+ * 'asc' => ['age' => SORT_ASC],
+ * 'desc' => ['age' => SORT_DESC],
+ * 'default' => SORT_ASC,
+ * 'label' => Inflector::camel2words('age'),
+ * ]
* ~~~
*
* The "user" attribute is a composite attribute:
@@ -119,10 +117,16 @@ class Sort extends Object
* - The "asc" and "desc" elements specify how to sort by the attribute in ascending
* and descending orders, respectively. Their values represent the actual columns and
* the directions by which the data should be sorted by.
- * - And the "default" element specifies if the attribute is not sorted currently,
- * in which direction it should be sorted (the default value is ascending order).
+ * - The "default" element specifies by which direction the attribute should be sorted
+ * if it is not currently sorted (the default value is ascending order).
+ * - The "label" element specifies what label should be used when calling [[link()]] to create
+ * a sort link. If not set, [[Inflector::camel2words()]] will be called to get a label.
+ * Note that it will not be HTML-encoded.
+ *
+ * Note that if the Sort object is already created, you can only use the full format
+ * to configure every attribute. Each attribute must include these elements: asc and desc.
*/
- public $attributes = array();
+ public $attributes = [];
/**
* @var string the name of the parameter that specifies which attributes to be sorted
* in which direction. Defaults to 'sort'.
@@ -139,15 +143,15 @@ class Sort extends Object
* The array keys are attribute names and the array values are the corresponding sort directions. For example,
*
* ~~~
- * array(
- * 'name' => Sort::ASC,
- * 'create_time' => Sort::DESC,
- * )
+ * [
+ * 'name' => SORT_ASC,
+ * 'create_time' => SORT_DESC,
+ * ]
* ~~~
*
* @see attributeOrders
*/
- public $defaults;
+ public $defaultOrder;
/**
* @var string the route of the controller action for displaying the sorted contents.
* If not set, it means using the currently requested route.
@@ -157,9 +161,9 @@ class Sort extends Object
* @var array separators used in the generated URL. This must be an array consisting of
* two elements. The first element specifies the character separating different
* attributes, while the second element specifies the character separating attribute name
- * and the corresponding sort direction. Defaults to `array('-', '.')`.
+ * and the corresponding sort direction. Defaults to `['.', '-']`.
*/
- public $separators = array('-', '.');
+ public $separators = ['.', '-'];
/**
* @var array parameters (name => value) that should be used to obtain the current sort directions
* and to create new sort URLs. If not set, $_GET will be used instead.
@@ -168,22 +172,52 @@ class Sort extends Object
* If the element does not exist, the [[defaults|default order]] will be used.
*
* @see sortVar
- * @see defaults
+ * @see defaultOrder
*/
public $params;
+ /**
+ * @var \yii\web\UrlManager the URL manager used for creating sort URLs. If not set,
+ * the "urlManager" application component will be used.
+ */
+ public $urlManager;
+
+ /**
+ * Normalizes the [[attributes]] property.
+ */
+ public function init()
+ {
+ $attributes = [];
+ foreach ($this->attributes as $name => $attribute) {
+ if (!is_array($attribute)) {
+ $attributes[$attribute] = [
+ 'asc' => [$attribute => SORT_ASC],
+ 'desc' => [$attribute => SORT_DESC],
+ ];
+ } elseif (!isset($attribute['asc'], $attribute['desc'])) {
+ $attributes[$name] = array_merge([
+ 'asc' => [$name => SORT_ASC],
+ 'desc' => [$name => SORT_DESC],
+ ], $attribute);
+ } else {
+ $attributes[$name] = $attribute;
+ }
+ }
+ $this->attributes = $attributes;
+ }
/**
* Returns the columns and their corresponding sort directions.
+ * @param boolean $recalculate whether to recalculate the sort directions
* @return array the columns (keys) and their corresponding sort directions (values).
* This can be passed to [[\yii\db\Query::orderBy()]] to construct a DB query.
*/
- public function getOrders()
+ public function getOrders($recalculate = false)
{
- $attributeOrders = $this->getAttributeOrders();
- $orders = array();
+ $attributeOrders = $this->getAttributeOrders($recalculate);
+ $orders = [];
foreach ($attributeOrders as $attribute => $direction) {
- $definition = $this->getAttribute($attribute);
- $columns = $definition[$direction === self::ASC ? 'asc' : 'desc'];
+ $definition = $this->attributes[$attribute];
+ $columns = $definition[$direction === SORT_ASC ? 'asc' : 'desc'];
foreach ($columns as $name => $dir) {
$orders[$name] = $dir;
}
@@ -192,48 +226,25 @@ class Sort extends Object
}
/**
- * Generates a hyperlink that links to the sort action to sort by the specified attribute.
- * Based on the sort direction, the CSS class of the generated hyperlink will be appended
- * with "asc" or "desc".
- * @param string $attribute the attribute name by which the data should be sorted by.
- * @param string $label the link label. Note that the label will not be HTML-encoded.
- * @param array $htmlOptions additional HTML attributes for the hyperlink tag
- * @return string the generated hyperlink
+ * @var array the currently requested sort order as computed by [[getAttributeOrders]].
*/
- public function link($attribute, $label, $htmlOptions = array())
- {
- if (($definition = $this->getAttribute($attribute)) === false) {
- return $label;
- }
-
- if (($direction = $this->getAttributeOrder($attribute)) !== null) {
- $class = $direction ? 'desc' : 'asc';
- if (isset($htmlOptions['class'])) {
- $htmlOptions['class'] .= ' ' . $class;
- } else {
- $htmlOptions['class'] = $class;
- }
- }
-
- $url = $this->createUrl($attribute);
-
- return Html::a($label, $url, $htmlOptions);
- }
-
private $_attributeOrders;
/**
* Returns the currently requested sort information.
* @param boolean $recalculate whether to recalculate the sort directions
* @return array sort directions indexed by attribute names.
- * Sort direction can be either [[Sort::ASC]] for ascending order or
- * [[Sort::DESC]] for descending order.
+ * Sort direction can be either `SORT_ASC` for ascending order or
+ * `SORT_DESC` for descending order.
*/
public function getAttributeOrders($recalculate = false)
{
if ($this->_attributeOrders === null || $recalculate) {
- $this->_attributeOrders = array();
- $params = $this->params === null ? $_GET : $this->params;
+ $this->_attributeOrders = [];
+ if (($params = $this->params) === null) {
+ $request = Yii::$app->getRequest();
+ $params = $request instanceof Request ? $request->get() : [];
+ }
if (isset($params[$this->sortVar]) && is_scalar($params[$this->sortVar])) {
$attributes = explode($this->separators[0], $params[$this->sortVar]);
foreach ($attributes as $attribute) {
@@ -244,16 +255,16 @@ class Sort extends Object
}
}
- if (($this->getAttribute($attribute)) !== false) {
- $this->_attributeOrders[$attribute] = $descending;
+ if (isset($this->attributes[$attribute])) {
+ $this->_attributeOrders[$attribute] = $descending ? SORT_DESC : SORT_ASC;
if (!$this->enableMultiSort) {
return $this->_attributeOrders;
}
}
}
}
- if (empty($this->_attributeOrders) && is_array($this->defaults)) {
- $this->_attributeOrders = $this->defaults;
+ if (empty($this->_attributeOrders) && is_array($this->defaultOrder)) {
+ $this->_attributeOrders = $this->defaultOrder;
}
}
return $this->_attributeOrders;
@@ -262,14 +273,54 @@ class Sort extends Object
/**
* Returns the sort direction of the specified attribute in the current request.
* @param string $attribute the attribute name
- * @return boolean|null Sort direction of the attribute. Can be either [[Sort::ASC]]
- * for ascending order or [[Sort::DESC]] for descending order. Null is returned
+ * @return boolean|null Sort direction of the attribute. Can be either `SORT_ASC`
+ * for ascending order or `SORT_DESC` for descending order. Null is returned
* if the attribute is invalid or does not need to be sorted.
*/
public function getAttributeOrder($attribute)
{
- $this->getAttributeOrders();
- return isset($this->_attributeOrders[$attribute]) ? $this->_attributeOrders[$attribute] : null;
+ $orders = $this->getAttributeOrders();
+ return isset($orders[$attribute]) ? $orders[$attribute] : null;
+ }
+
+ /**
+ * Generates a hyperlink that links to the sort action to sort by the specified attribute.
+ * Based on the sort direction, the CSS class of the generated hyperlink will be appended
+ * with "asc" or "desc".
+ * @param string $attribute the attribute name by which the data should be sorted by.
+ * @param array $options additional HTML attributes for the hyperlink tag.
+ * There is one special attribute `label` which will be used as the label of the hyperlink.
+ * If this is not set, the label defined in [[attributes]] will be used.
+ * If no label is defined, [[yii\helpers\Inflector::camel2words()]] will be called to get a label.
+ * Note that it will not be HTML-encoded.
+ * @return string the generated hyperlink
+ * @throws InvalidConfigException if the attribute is unknown
+ */
+ public function link($attribute, $options = [])
+ {
+ if (($direction = $this->getAttributeOrder($attribute)) !== null) {
+ $class = $direction === SORT_DESC ? 'desc' : 'asc';
+ if (isset($options['class'])) {
+ $options['class'] .= ' ' . $class;
+ } else {
+ $options['class'] = $class;
+ }
+ }
+
+ $url = $this->createUrl($attribute);
+ $options['data-sort'] = $this->createSortVar($attribute);
+
+ if (isset($options['label'])) {
+ $label = $options['label'];
+ unset($options['label']);
+ } else {
+ if (isset($this->attributes[$attribute]['label'])) {
+ $label = $this->attributes[$attribute]['label'];
+ } else {
+ $label = Inflector::camel2words($attribute);
+ }
+ }
+ return Html::a($label, $url, $options);
}
/**
@@ -278,60 +329,70 @@ class Sort extends Object
* For example, if the current page already sorts the data by the specified attribute in ascending order,
* then the URL created will lead to a page that sorts the data by the specified attribute in descending order.
* @param string $attribute the attribute name
- * @return string|boolean the URL for sorting. False if the attribute is invalid.
+ * @param boolean $absolute whether to create an absolute URL. Defaults to `false`.
+ * @return string the URL for sorting. False if the attribute is invalid.
+ * @throws InvalidConfigException if the attribute is unknown
* @see attributeOrders
* @see params
*/
- public function createUrl($attribute)
+ public function createUrl($attribute, $absolute = false)
{
- if (($definition = $this->getAttribute($attribute)) === false) {
- return false;
+ if (($params = $this->params) === null) {
+ $request = Yii::$app->getRequest();
+ $params = $request instanceof Request ? $request->get() : [];
}
+ $params[$this->sortVar] = $this->createSortVar($attribute);
+ $route = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
+ $urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;
+ if ($absolute) {
+ return $urlManager->createAbsoluteUrl($route, $params);
+ } else {
+ return $urlManager->createUrl($route, $params);
+ }
+ }
+
+ /**
+ * Creates the sort variable for the specified attribute.
+ * The newly created sort variable can be used to create a URL that will lead to
+ * sorting by the specified attribute.
+ * @param string $attribute the attribute name
+ * @return string the value of the sort variable
+ * @throws InvalidConfigException if the specified attribute is not defined in [[attributes]]
+ */
+ public function createSortVar($attribute)
+ {
+ if (!isset($this->attributes[$attribute])) {
+ throw new InvalidConfigException("Unknown attribute: $attribute");
+ }
+ $definition = $this->attributes[$attribute];
$directions = $this->getAttributeOrders();
if (isset($directions[$attribute])) {
- $descending = !$directions[$attribute];
+ $direction = $directions[$attribute] === SORT_DESC ? SORT_ASC : SORT_DESC;
unset($directions[$attribute]);
- } elseif (isset($definition['default'])) {
- $descending = $definition['default'] === 'desc';
} else {
- $descending = false;
+ $direction = isset($definition['default']) ? $definition['default'] : SORT_ASC;
}
if ($this->enableMultiSort) {
- $directions = array_merge(array($attribute => $descending), $directions);
+ $directions = array_merge([$attribute => $direction], $directions);
} else {
- $directions = array($attribute => $descending);
+ $directions = [$attribute => $direction];
}
- $sorts = array();
- foreach ($directions as $attribute => $descending) {
- $sorts[] = $descending ? $attribute . $this->separators[1] . $this->descTag : $attribute;
+ $sorts = [];
+ foreach ($directions as $attribute => $direction) {
+ $sorts[] = $direction === SORT_DESC ? $attribute . $this->separators[1] . $this->descTag : $attribute;
}
- $params = $this->params === null ? $_GET : $this->params;
- $params[$this->sortVar] = implode($this->separators[0], $sorts);
- $route = $this->route === null ? Yii::$app->controller->route : $this->route;
-
- return Yii::$app->getUrlManager()->createUrl($route, $params);
+ return implode($this->separators[0], $sorts);
}
/**
- * Returns the attribute definition of the specified name.
+ * Returns a value indicating whether the sort definition supports sorting by the named attribute.
* @param string $name the attribute name
- * @return array|boolean the sort definition (column names => sort directions).
- * False is returned if the attribute cannot be sorted.
- * @see attributes
+ * @return boolean whether the sort definition supports sorting by the named attribute.
*/
- public function getAttribute($name)
+ public function hasAttribute($name)
{
- if (isset($this->attributes[$name])) {
- return $this->attributes[$name];
- } elseif (in_array($name, $this->attributes, true)) {
- return array(
- 'asc' => array($name => self::ASC),
- 'desc' => array($name => self::DESC),
- );
- } else {
- return false;
- }
+ return isset($this->attributes[$name]);
}
}
diff --git a/framework/yii/data/SqlDataProvider.php b/framework/yii/data/SqlDataProvider.php
new file mode 100644
index 0000000..5517aa1
--- /dev/null
+++ b/framework/yii/data/SqlDataProvider.php
@@ -0,0 +1,159 @@
+db->createCommand('
+ * SELECT COUNT(*) FROM tbl_user WHERE status=:status
+ * ', [':status' => 1])->queryScalar();
+ *
+ * $dataProvider = new SqlDataProvider([
+ * 'sql' => 'SELECT * FROM tbl_user WHERE status=:status',
+ * 'params' => [':status' => 1],
+ * 'totalCount' => $count,
+ * 'sort' => [
+ * 'attributes' => [
+ * 'age',
+ * 'name' => [
+ * 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
+ * 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
+ * 'default' => SORT_DESC,
+ * 'label' => 'Name',
+ * ],
+ * ],
+ * ],
+ * 'pagination' => [
+ * 'pageSize' => 20,
+ * ],
+ * ]);
+ *
+ * // get the user records in the current page
+ * $models = $dataProvider->getModels();
+ * ~~~
+ *
+ * Note: if you want to use the pagination feature, you must configure the [[totalCount]] property
+ * to be the total number of rows (without pagination). And if you want to use the sorting feature,
+ * you must configure the [[sort]] property so that the provider knows which columns can be sorted.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class SqlDataProvider extends BaseDataProvider
+{
+ /**
+ * @var Connection|string the DB connection object or the application component ID of the DB connection.
+ */
+ public $db = 'db';
+ /**
+ * @var string the SQL statement to be used for fetching data rows.
+ */
+ public $sql;
+ /**
+ * @var array parameters (name=>value) to be bound to the SQL statement.
+ */
+ public $params = [];
+ /**
+ * @var string|callable the column that is used as the key of the data models.
+ * This can be either a column name, or a callable that returns the key value of a given data model.
+ *
+ * If this is not set, the keys of the [[models]] array will be used.
+ */
+ public $key;
+
+
+ /**
+ * Initializes the DB connection component.
+ * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
+ * @throws InvalidConfigException if [[db]] is invalid.
+ */
+ public function init()
+ {
+ parent::init();
+ if (is_string($this->db)) {
+ $this->db = Yii::$app->getComponent($this->db);
+ }
+ if (!$this->db instanceof Connection) {
+ throw new InvalidConfigException('The "db" property must be a valid DB Connection application component.');
+ }
+ if ($this->sql === null) {
+ throw new InvalidConfigException('The "sql" property must be set.');
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function prepareModels()
+ {
+ $sql = $this->sql;
+ $qb = $this->db->getQueryBuilder();
+ if (($sort = $this->getSort()) !== false) {
+ $orderBy = $qb->buildOrderBy($sort->getOrders());
+ if (!empty($orderBy)) {
+ $orderBy = substr($orderBy, 9);
+ if (preg_match('/\s+order\s+by\s+[\w\s,\.]+$/i', $sql)) {
+ $sql .= ', ' . $orderBy;
+ } else {
+ $sql .= ' ORDER BY ' . $orderBy;
+ }
+ }
+ }
+
+ if (($pagination = $this->getPagination()) !== false) {
+ $pagination->totalCount = $this->getTotalCount();
+ $sql .= ' ' . $qb->buildLimit($pagination->getLimit(), $pagination->getOffset());
+ }
+
+ return $this->db->createCommand($sql, $this->params)->queryAll();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function prepareKeys($models)
+ {
+ $keys = [];
+ if ($this->key !== null) {
+ foreach ($models as $model) {
+ if (is_string($this->key)) {
+ $keys[] = $model[$this->key];
+ } else {
+ $keys[] = call_user_func($this->key, $model);
+ }
+ }
+ return $keys;
+ } else {
+ return array_keys($models);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function prepareTotalCount()
+ {
+ return 0;
+ }
+}
diff --git a/framework/yii/db/ActiveQuery.php b/framework/yii/db/ActiveQuery.php
index cfe9c3f..26b0c6e 100644
--- a/framework/yii/db/ActiveQuery.php
+++ b/framework/yii/db/ActiveQuery.php
@@ -8,16 +8,10 @@
namespace yii\db;
-use yii\db\Connection;
-use yii\db\Command;
-use yii\db\QueryBuilder;
-use yii\db\Expression;
-
/**
* ActiveQuery represents a DB query associated with an Active Record class.
*
- * ActiveQuery instances are usually created by [[ActiveRecord::find()]], [[ActiveRecord::findBySql()]]
- * and [[ActiveRecord::count()]].
+ * ActiveQuery instances are usually created by [[ActiveRecord::find()]] and [[ActiveRecord::findBySql()]].
*
* ActiveQuery mainly provides the following methods to retrieve the query results:
*
@@ -29,6 +23,7 @@ use yii\db\Expression;
* - [[min()]]: returns the min over the specified column.
* - [[max()]]: returns the max over the specified column.
* - [[scalar()]]: returns the value of the first column in the first row of the query result.
+ * - [[column()]]: returns the value of the first column in the query result.
* - [[exists()]]: returns a value indicating whether the query result has data or not.
*
* Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
@@ -47,28 +42,13 @@ use yii\db\Expression;
* ~~~
*
* @author Qiang Xue
+ * @author Carsten Brandt
* @since 2.0
*/
-class ActiveQuery extends Query
+class ActiveQuery extends Query implements ActiveQueryInterface
{
- /**
- * @var string the name of the ActiveRecord class.
- */
- public $modelClass;
- /**
- * @var array list of relations that this query should be performed with
- */
- public $with;
- /**
- * @var string the name of the column by which query results should be indexed by.
- * This is only used when the query result is returned as an array when calling [[all()]].
- */
- public $indexBy;
- /**
- * @var boolean whether to return each record as an array. If false (default), an object
- * of [[modelClass]] will be created to represent each record.
- */
- public $asArray;
+ use ActiveQueryTrait;
+
/**
* @var string the SQL statement to be executed for retrieving AR records.
* This is set by [[ActiveRecord::findBySql()]].
@@ -77,150 +57,102 @@ class ActiveQuery extends Query
/**
- * PHP magic method.
- * This method allows calling static method defined in [[modelClass]] via this query object.
- * It is mainly implemented for supporting the feature of scope.
- * @param string $name the method name to be called
- * @param array $params the parameters passed to the method
- * @return mixed the method return result
- */
- public function __call($name, $params)
- {
- if (method_exists($this->modelClass, $name)) {
- array_unshift($params, $this);
- call_user_func_array(array($this->modelClass, $name), $params);
- return $this;
- } else {
- return parent::__call($name, $params);
- }
- }
-
- /**
* Executes query and returns all results as an array.
+ * @param Connection $db the DB connection used to create the DB command.
+ * If null, the DB connection returned by [[modelClass]] will be used.
* @return array the query results. If the query results in nothing, an empty array will be returned.
*/
- public function all()
+ public function all($db = null)
{
- $command = $this->createCommand();
+ $command = $this->createCommand($db);
$rows = $command->queryAll();
if (!empty($rows)) {
$models = $this->createModels($rows);
+ if (!empty($this->join) && $this->indexBy === null) {
+ $models = $this->removeDuplicatedModels($models);
+ }
if (!empty($this->with)) {
- $this->populateRelations($models, $this->with);
+ $this->findWith($this->with, $models);
}
return $models;
} else {
- return array();
+ return [];
}
}
/**
+ * Removes duplicated models by checking their primary key values.
+ * This method is mainly called when a join query is performed, which may cause duplicated rows being returned.
+ * @param array $models the models to be checked
+ * @return array the distinctive models
+ */
+ private function removeDuplicatedModels($models)
+ {
+ $hash = [];
+ /** @var ActiveRecord $class */
+ $class = $this->modelClass;
+ $pks = $class::primaryKey();
+
+ if (count($pks) > 1) {
+ foreach ($models as $i => $model) {
+ $key = [];
+ foreach ($pks as $pk) {
+ $key[] = $model[$pk];
+ }
+ $key = serialize($key);
+ if (isset($hash[$key])) {
+ unset($models[$i]);
+ } else {
+ $hash[$key] = true;
+ }
+ }
+ } else {
+ $pk = reset($pks);
+ foreach ($models as $i => $model) {
+ $key = $model[$pk];
+ if (isset($hash[$key])) {
+ unset($models[$i]);
+ } else {
+ $hash[$key] = true;
+ }
+ }
+ }
+
+ return array_values($models);
+ }
+
+ /**
* Executes query and returns a single row of result.
+ * @param Connection $db the DB connection used to create the DB command.
+ * If null, the DB connection returned by [[modelClass]] will be used.
* @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]],
* the query result may be either an array or an ActiveRecord object. Null will be returned
* if the query results in nothing.
*/
- public function one()
+ public function one($db = null)
{
- $command = $this->createCommand();
- $row = $command->queryRow();
- if ($row !== false && !$this->asArray) {
- /** @var $class ActiveRecord */
- $class = $this->modelClass;
- $model = $class::create($row);
+ $command = $this->createCommand($db);
+ $row = $command->queryOne();
+ if ($row !== false) {
+ if ($this->asArray) {
+ $model = $row;
+ } else {
+ /** @var ActiveRecord $class */
+ $class = $this->modelClass;
+ $model = $class::create($row);
+ }
if (!empty($this->with)) {
- $models = array($model);
- $this->populateRelations($models, $this->with);
+ $models = [$model];
+ $this->findWith($this->with, $models);
$model = $models[0];
}
return $model;
} else {
- return $row === false ? null : $row;
+ return null;
}
}
/**
- * Returns the number of records.
- * @param string $q the COUNT expression. Defaults to '*'.
- * Make sure you properly quote column names.
- * @return integer number of records
- */
- public function count($q = '*')
- {
- $this->select = array("COUNT($q)");
- return $this->createCommand()->queryScalar();
- }
-
- /**
- * Returns the sum of the specified column values.
- * @param string $q the column name or expression.
- * Make sure you properly quote column names.
- * @return integer the sum of the specified column values
- */
- public function sum($q)
- {
- $this->select = array("SUM($q)");
- return $this->createCommand()->queryScalar();
- }
-
- /**
- * Returns the average of the specified column values.
- * @param string $q the column name or expression.
- * Make sure you properly quote column names.
- * @return integer the average of the specified column values.
- */
- public function average($q)
- {
- $this->select = array("AVG($q)");
- return $this->createCommand()->queryScalar();
- }
-
- /**
- * Returns the minimum of the specified column values.
- * @param string $q the column name or expression.
- * Make sure you properly quote column names.
- * @return integer the minimum of the specified column values.
- */
- public function min($q)
- {
- $this->select = array("MIN($q)");
- return $this->createCommand()->queryScalar();
- }
-
- /**
- * Returns the maximum of the specified column values.
- * @param string $q the column name or expression.
- * Make sure you properly quote column names.
- * @return integer the maximum of the specified column values.
- */
- public function max($q)
- {
- $this->select = array("MAX($q)");
- return $this->createCommand()->queryScalar();
- }
-
- /**
- * Returns the query result as a scalar value.
- * The value returned will be the first column in the first row of the query results.
- * @return string|boolean the value of the first column in the first row of the query result.
- * False is returned if the query result is empty.
- */
- public function scalar()
- {
- return $this->createCommand()->queryScalar();
- }
-
- /**
- * Returns a value indicating whether the query result contains any row of data.
- * @return boolean whether the query result contains any row of data.
- */
- public function exists()
- {
- $this->select = array(new Expression('1'));
- return $this->scalar() !== false;
- }
-
- /**
* Creates a DB command that can be used to execute this query.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
@@ -228,156 +160,250 @@ class ActiveQuery extends Query
*/
public function createCommand($db = null)
{
- /** @var $modelClass ActiveRecord */
+ /** @var ActiveRecord $modelClass */
$modelClass = $this->modelClass;
if ($db === null) {
$db = $modelClass::getDb();
}
+
if ($this->sql === null) {
+ $select = $this->select;
+ $from = $this->from;
+
if ($this->from === null) {
$tableName = $modelClass::tableName();
if ($this->select === null && !empty($this->join)) {
- $this->select = array("$tableName.*");
+ $this->select = ["$tableName.*"];
}
- $this->from = array($tableName);
+ $this->from = [$tableName];
}
- /** @var $qb QueryBuilder */
- $qb = $db->getQueryBuilder();
- $this->sql = $qb->build($this);
- }
- return $db->createCommand($this->sql, $this->params);
- }
+ list ($sql, $params) = $db->getQueryBuilder()->build($this);
- /**
- * Sets the [[asArray]] property.
- * @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
- * @return ActiveQuery the query object itself
- */
- public function asArray($value = true)
- {
- $this->asArray = $value;
- return $this;
+ $this->select = $select;
+ $this->from = $from;
+ } else {
+ $sql = $this->sql;
+ $params = $this->params;
+ }
+ return $db->createCommand($sql, $params);
}
/**
- * Specifies the relations with which this query should be performed.
+ * Joins with the specified relations.
+ *
+ * This method allows you to reuse existing relation definitions to perform JOIN queries.
+ * Based on the definition of the specified relation(s), the method will append one or multiple
+ * JOIN statements to the current query.
*
- * The parameters to this method can be either one or multiple strings, or a single array
- * of relation names and the optional callbacks to customize the relations.
+ * If the `$eagerLoading` parameter is true, the method will also eager loading the specified relations,
+ * which is equivalent to calling [[with()]] using the specified relations.
*
- * The followings are some usage examples:
+ * Note that because a JOIN query will be performed, you are responsible to disambiguate column names.
*
- * ~~~
- * // find customers together with their orders and country
- * Customer::find()->with('orders', 'country')->all();
- * // find customers together with their country and orders of status 1
- * Customer::find()->with(array(
- * 'orders' => function($query) {
- * $query->andWhere('status = 1');
- * },
- * 'country',
- * ))->all();
- * ~~~
+ * This method differs from [[with()]] in that it will build up and execute a JOIN SQL statement
+ * for the primary table. And when `$eagerLoading` is true, it will call [[with()]] in addition with the specified relations.
*
- * @return ActiveQuery the query object itself
+ * @param array $with the relations to be joined. Each array element represents a single relation.
+ * The array keys are relation names, and the array values are the corresponding anonymous functions that
+ * can be used to modify the relation queries on-the-fly. If a relation query does not need modification,
+ * you may use the relation name as the array value. Sub-relations can also be specified (see [[with()]]).
+ * For example,
+ *
+ * ```php
+ * // find all orders that contain books, and eager loading "books"
+ * Order::find()->joinWith('books', true, 'INNER JOIN')->all();
+ * // find all orders, eager loading "books", and sort the orders and books by the book names.
+ * Order::find()->joinWith([
+ * 'books' => function ($query) {
+ * $query->orderBy('tbl_item.name');
+ * }
+ * ])->all();
+ * ```
+ *
+ * @param boolean|array $eagerLoading whether to eager load the relations specified in `$with`.
+ * When this is a boolean, it applies to all relations specified in `$with`. Use an array
+ * to explicitly list which relations in `$with` need to be eagerly loaded.
+ * @param string|array $joinType the join type of the relations specified in `$with`.
+ * When this is a string, it applies to all relations specified in `$with`. Use an array
+ * in the format of `relationName => joinType` to specify different join types for different relations.
+ * @return static the query object itself
*/
- public function with()
+ public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN')
{
- $this->with = func_get_args();
- if (isset($this->with[0]) && is_array($this->with[0])) {
- // the parameter is given as an array
- $this->with = $this->with[0];
+ $with = (array)$with;
+ $this->joinWithRelations(new $this->modelClass, $with, $joinType);
+
+ if (is_array($eagerLoading)) {
+ foreach ($with as $name => $callback) {
+ if (is_integer($name)) {
+ if (!in_array($callback, $eagerLoading, true)) {
+ unset($with[$name]);
+ }
+ } elseif (!in_array($name, $eagerLoading, true)) {
+ unset($with[$name]);
+ }
+ }
+ $this->with($with);
+ } elseif ($eagerLoading) {
+ $this->with($with);
}
return $this;
}
/**
- * Sets the [[indexBy]] property.
- * @param string $column the name of the column by which the query results should be indexed by.
- * @return ActiveQuery the query object itself
+ * Inner joins with the specified relations.
+ * This is a shortcut method to [[joinWith()]] with the join type set as "INNER JOIN".
+ * Please refer to [[joinWith()]] for detailed usage of this method.
+ * @param array $with the relations to be joined with
+ * @param boolean|array $eagerLoading whether to eager loading the relations
+ * @return static the query object itself
+ * @see joinWith()
*/
- public function indexBy($column)
+ public function innerJoinWith($with, $eagerLoading = true)
{
- $this->indexBy = $column;
- return $this;
+ return $this->joinWith($with, $eagerLoading, 'INNER JOIN');
}
- private function createModels($rows)
+ /**
+ * Modifies the current query by adding join fragments based on the given relations.
+ * @param ActiveRecord $model the primary model
+ * @param array $with the relations to be joined
+ * @param string|array $joinType the join type
+ */
+ private function joinWithRelations($model, $with, $joinType)
{
- $models = array();
- if ($this->asArray) {
- if ($this->indexBy === null) {
- return $rows;
- }
- foreach ($rows as $row) {
- $models[$row[$this->indexBy]] = $row;
+ $relations = [];
+
+ foreach ($with as $name => $callback) {
+ if (is_integer($name)) {
+ $name = $callback;
+ $callback = null;
}
- } else {
- /** @var $class ActiveRecord */
- $class = $this->modelClass;
- if ($this->indexBy === null) {
- foreach ($rows as $row) {
- $models[] = $class::create($row);
+
+ $primaryModel = $model;
+ $parent = $this;
+ $prefix = '';
+ while (($pos = strpos($name, '.')) !== false) {
+ $childName = substr($name, $pos + 1);
+ $name = substr($name, 0, $pos);
+ $fullName = $prefix === '' ? $name : "$prefix.$name";
+ if (!isset($relations[$fullName])) {
+ $relations[$fullName] = $relation = $primaryModel->getRelation($name);
+ $this->joinWithRelation($parent, $relation, $this->getJoinType($joinType, $fullName));
+ } else {
+ $relation = $relations[$fullName];
}
- } else {
- foreach ($rows as $row) {
- $model = $class::create($row);
- $models[$model->{$this->indexBy}] = $model;
+ $primaryModel = new $relation->modelClass;
+ $parent = $relation;
+ $prefix = $fullName;
+ $name = $childName;
+ }
+
+ $fullName = $prefix === '' ? $name : "$prefix.$name";
+ if (!isset($relations[$fullName])) {
+ $relations[$fullName] = $relation = $primaryModel->getRelation($name);
+ if ($callback !== null) {
+ call_user_func($callback, $relation);
}
+ $this->joinWithRelation($parent, $relation, $this->getJoinType($joinType, $fullName));
}
}
- return $models;
}
- private function populateRelations(&$models, $with)
+ /**
+ * Returns the join type based on the given join type parameter and the relation name.
+ * @param string|array $joinType the given join type(s)
+ * @param string $name relation name
+ * @return string the real join type
+ */
+ private function getJoinType($joinType, $name)
{
- $primaryModel = new $this->modelClass;
- $relations = $this->normalizeRelations($primaryModel, $with);
- foreach ($relations as $name => $relation) {
- if ($relation->asArray === null) {
- // inherit asArray from primary query
- $relation->asArray = $this->asArray;
- }
- $relation->findWith($name, $models);
+ if (is_array($joinType) && isset($joinType[$name])) {
+ return $joinType[$name];
+ } else {
+ return is_string($joinType) ? $joinType : 'INNER JOIN';
}
}
/**
- * @param ActiveRecord $model
- * @param array $with
- * @return ActiveRelation[]
+ * Returns the table name used by the specified active query.
+ * @param ActiveQuery $query
+ * @return string the table name
*/
- private function normalizeRelations($model, $with)
+ private function getQueryTableName($query)
{
- $relations = array();
- foreach ($with as $name => $callback) {
- if (is_integer($name)) {
- $name = $callback;
- $callback = null;
- }
- if (($pos = strpos($name, '.')) !== false) {
- // with sub-relations
- $childName = substr($name, $pos + 1);
- $name = substr($name, 0, $pos);
- } else {
- $childName = null;
- }
+ if (empty($query->from)) {
+ /** @var ActiveRecord $modelClass */
+ $modelClass = $query->modelClass;
+ return $modelClass::tableName();
+ } else {
+ return reset($query->from);
+ }
+ }
- $t = strtolower($name);
- if (!isset($relations[$t])) {
- $relation = $model->getRelation($name);
- $relation->primaryModel = null;
- $relations[$t] = $relation;
- } else {
- $relation = $relations[$t];
+ /**
+ * Joins a parent query with a child query.
+ * The current query object will be modified accordingly.
+ * @param ActiveQuery $parent
+ * @param ActiveRelation $child
+ * @param string $joinType
+ */
+ private function joinWithRelation($parent, $child, $joinType)
+ {
+ $via = $child->via;
+ $child->via = null;
+ if ($via instanceof ActiveRelation) {
+ // via table
+ $this->joinWithRelation($parent, $via, $joinType);
+ $this->joinWithRelation($via, $child, $joinType);
+ return;
+ } elseif (is_array($via)) {
+ // via relation
+ $this->joinWithRelation($parent, $via[1], $joinType);
+ $this->joinWithRelation($via[1], $child, $joinType);
+ return;
+ }
+
+ $parentTable = $this->getQueryTableName($parent);
+ $childTable = $this->getQueryTableName($child);
+
+
+ if (!empty($child->link)) {
+ $on = [];
+ foreach ($child->link as $childColumn => $parentColumn) {
+ $on[] = '{{' . $parentTable . "}}.[[$parentColumn]] = {{" . $childTable . "}}.[[$childColumn]]";
}
+ $on = implode(' AND ', $on);
+ } else {
+ $on = '';
+ }
+ $this->join($joinType, $childTable, $on);
- if (isset($childName)) {
- $relation->with[$childName] = $callback;
- } elseif ($callback !== null) {
- call_user_func($callback, $relation);
+
+ if (!empty($child->where)) {
+ $this->andWhere($child->where);
+ }
+ if (!empty($child->having)) {
+ $this->andHaving($child->having);
+ }
+ if (!empty($child->orderBy)) {
+ $this->addOrderBy($child->orderBy);
+ }
+ if (!empty($child->groupBy)) {
+ $this->addGroupBy($child->groupBy);
+ }
+ if (!empty($child->params)) {
+ $this->addParams($child->params);
+ }
+ if (!empty($child->join)) {
+ foreach ($child->join as $join) {
+ $this->join[] = $join;
+ }
+ }
+ if (!empty($child->union)) {
+ foreach ($child->union as $union) {
+ $this->union[] = $union;
}
}
- return $relations;
}
}
diff --git a/framework/yii/db/ActiveQueryInterface.php b/framework/yii/db/ActiveQueryInterface.php
new file mode 100644
index 0000000..a2e132f
--- /dev/null
+++ b/framework/yii/db/ActiveQueryInterface.php
@@ -0,0 +1,77 @@
+
+ * @author Carsten Brandt
+ * @since 2.0
+ */
+interface ActiveQueryInterface extends QueryInterface
+{
+ /**
+ * Sets the [[asArray]] property.
+ * @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
+ * @return static the query object itself
+ */
+ public function asArray($value = true);
+
+ /**
+ * Sets the [[indexBy]] property.
+ * @param string|callable $column the name of the column by which the query results should be indexed by.
+ * This can also be a callable (e.g. anonymous function) that returns the index value based on the given
+ * row or model data. The signature of the callable should be:
+ *
+ * ~~~
+ * // $model is an AR instance when `asArray` is false,
+ * // or an array of column values when `asArray` is true.
+ * function ($model)
+ * {
+ * // return the index value corresponding to $model
+ * }
+ * ~~~
+ *
+ * @return static the query object itself
+ */
+ public function indexBy($column);
+
+ /**
+ * Specifies the relations with which this query should be performed.
+ *
+ * The parameters to this method can be either one or multiple strings, or a single array
+ * of relation names and the optional callbacks to customize the relations.
+ *
+ * A relation name can refer to a relation defined in [[modelClass]]
+ * or a sub-relation that stands for a relation of a related record.
+ * For example, `orders.address` means the `address` relation defined
+ * in the model class corresponding to the `orders` relation.
+ *
+ * The followings are some usage examples:
+ *
+ * ~~~
+ * // find customers together with their orders and country
+ * Customer::find()->with('orders', 'country')->all();
+ * // find customers together with their orders and the orders' shipping address
+ * Customer::find()->with('orders.address')->all();
+ * // find customers together with their country and orders of status 1
+ * Customer::find()->with([
+ * 'orders' => function($query) {
+ * $query->andWhere('status = 1');
+ * },
+ * 'country',
+ * ])->all();
+ * ~~~
+ *
+ * @return static the query object itself
+ */
+ public function with();
+}
diff --git a/framework/yii/db/ActiveQueryTrait.php b/framework/yii/db/ActiveQueryTrait.php
new file mode 100644
index 0000000..c8ba773
--- /dev/null
+++ b/framework/yii/db/ActiveQueryTrait.php
@@ -0,0 +1,201 @@
+
+ * @author Carsten Brandt
+ * @since 2.0
+ */
+trait ActiveQueryTrait
+{
+ /**
+ * @var string the name of the ActiveRecord class.
+ */
+ public $modelClass;
+ /**
+ * @var array a list of relations that this query should be performed with
+ */
+ public $with;
+ /**
+ * @var boolean whether to return each record as an array. If false (default), an object
+ * of [[modelClass]] will be created to represent each record.
+ */
+ public $asArray;
+
+
+ /**
+ * PHP magic method.
+ * This method allows calling static method defined in [[modelClass]] via this query object.
+ * It is mainly implemented for supporting the feature of scope.
+ * @param string $name the method name to be called
+ * @param array $params the parameters passed to the method
+ * @return mixed the method return result
+ */
+ public function __call($name, $params)
+ {
+ if (method_exists($this->modelClass, $name)) {
+ array_unshift($params, $this);
+ call_user_func_array([$this->modelClass, $name], $params);
+ return $this;
+ } else {
+ return parent::__call($name, $params);
+ }
+ }
+
+ /**
+ * Sets the [[asArray]] property.
+ * @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
+ * @return static the query object itself
+ */
+ public function asArray($value = true)
+ {
+ $this->asArray = $value;
+ return $this;
+ }
+
+ /**
+ * Specifies the relations with which this query should be performed.
+ *
+ * The parameters to this method can be either one or multiple strings, or a single array
+ * of relation names and the optional callbacks to customize the relations.
+ *
+ * A relation name can refer to a relation defined in [[modelClass]]
+ * or a sub-relation that stands for a relation of a related record.
+ * For example, `orders.address` means the `address` relation defined
+ * in the model class corresponding to the `orders` relation.
+ *
+ * The followings are some usage examples:
+ *
+ * ~~~
+ * // find customers together with their orders and country
+ * Customer::find()->with('orders', 'country')->all();
+ * // find customers together with their orders and the orders' shipping address
+ * Customer::find()->with('orders.address')->all();
+ * // find customers together with their country and orders of status 1
+ * Customer::find()->with([
+ * 'orders' => function($query) {
+ * $query->andWhere('status = 1');
+ * },
+ * 'country',
+ * ])->all();
+ * ~~~
+ *
+ * @return static the query object itself
+ */
+ public function with()
+ {
+ $this->with = func_get_args();
+ if (isset($this->with[0]) && is_array($this->with[0])) {
+ // the parameter is given as an array
+ $this->with = $this->with[0];
+ }
+ return $this;
+ }
+
+ /**
+ * Converts found rows into model instances
+ * @param array $rows
+ * @return array|ActiveRecord[]
+ */
+ private function createModels($rows)
+ {
+ $models = [];
+ if ($this->asArray) {
+ if ($this->indexBy === null) {
+ return $rows;
+ }
+ foreach ($rows as $row) {
+ if (is_string($this->indexBy)) {
+ $key = $row[$this->indexBy];
+ } else {
+ $key = call_user_func($this->indexBy, $row);
+ }
+ $models[$key] = $row;
+ }
+ } else {
+ /** @var ActiveRecord $class */
+ $class = $this->modelClass;
+ if ($this->indexBy === null) {
+ foreach ($rows as $row) {
+ $models[] = $class::create($row);
+ }
+ } else {
+ foreach ($rows as $row) {
+ $model = $class::create($row);
+ if (is_string($this->indexBy)) {
+ $key = $model->{$this->indexBy};
+ } else {
+ $key = call_user_func($this->indexBy, $model);
+ }
+ $models[$key] = $model;
+ }
+ }
+ }
+ return $models;
+ }
+
+ /**
+ * Finds records corresponding to one or multiple relations and populates them into the primary models.
+ * @param array $with a list of relations that this query should be performed with. Please
+ * refer to [[with()]] for details about specifying this parameter.
+ * @param array $models the primary models (can be either AR instances or arrays)
+ */
+ public function findWith($with, &$models)
+ {
+ $primaryModel = new $this->modelClass;
+ $relations = $this->normalizeRelations($primaryModel, $with);
+ foreach ($relations as $name => $relation) {
+ if ($relation->asArray === null) {
+ // inherit asArray from primary query
+ $relation->asArray = $this->asArray;
+ }
+ $relation->populateRelation($name, $models);
+ }
+ }
+
+ /**
+ * @param ActiveRecord $model
+ * @param array $with
+ * @return ActiveRelationInterface[]
+ */
+ private function normalizeRelations($model, $with)
+ {
+ $relations = [];
+ foreach ($with as $name => $callback) {
+ if (is_integer($name)) {
+ $name = $callback;
+ $callback = null;
+ }
+ if (($pos = strpos($name, '.')) !== false) {
+ // with sub-relations
+ $childName = substr($name, $pos + 1);
+ $name = substr($name, 0, $pos);
+ } else {
+ $childName = null;
+ }
+
+ if (!isset($relations[$name])) {
+ $relation = $model->getRelation($name);
+ $relation->primaryModel = null;
+ $relations[$name] = $relation;
+ } else {
+ $relation = $relations[$name];
+ }
+
+ if (isset($childName)) {
+ $relation->with[$childName] = $callback;
+ } elseif ($callback !== null) {
+ call_user_func($callback, $relation);
+ }
+ }
+ return $relations;
+ }
+}
diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php
index 6faebbf..cc67224 100644
--- a/framework/yii/db/ActiveRecord.php
+++ b/framework/yii/db/ActiveRecord.php
@@ -9,100 +9,37 @@
namespace yii\db;
use yii\base\InvalidConfigException;
-use yii\base\Model;
-use yii\base\InvalidParamException;
-use yii\base\ModelEvent;
-use yii\base\UnknownMethodException;
-use yii\base\InvalidCallException;
-use yii\db\Connection;
-use yii\db\TableSchema;
-use yii\db\Expression;
-use yii\helpers\StringHelper;
use yii\helpers\Inflector;
+use yii\helpers\StringHelper;
/**
* ActiveRecord is the base class for classes representing relational data in terms of objects.
*
* @include @yii/db/ActiveRecord.md
*
- * @property Connection $db the database connection used by this AR class.
- * @property TableSchema $tableSchema the schema information of the DB table associated with this AR class.
- * @property array $oldAttributes the old attribute values (name-value pairs).
- * @property array $dirtyAttributes the changed attribute values (name-value pairs).
- * @property boolean $isNewRecord whether the record is new and should be inserted when calling [[save()]].
- * @property mixed $primaryKey the primary key value.
- * @property mixed $oldPrimaryKey the old primary key value.
- *
* @author Qiang Xue
+ * @author Carsten Brandt
* @since 2.0
*/
-class ActiveRecord extends Model
+class ActiveRecord extends BaseActiveRecord
{
/**
- * @event Event an event that is triggered when the record is initialized via [[init()]].
- */
- const EVENT_INIT = 'init';
- /**
- * @event Event an event that is triggered after the record is created and populated with query result.
- */
- const EVENT_AFTER_FIND = 'afterFind';
- /**
- * @event ModelEvent an event that is triggered before inserting a record.
- * You may set [[ModelEvent::isValid]] to be false to stop the insertion.
- */
- const EVENT_BEFORE_INSERT = 'beforeInsert';
- /**
- * @event Event an event that is triggered after a record is inserted.
- */
- const EVENT_AFTER_INSERT = 'afterInsert';
- /**
- * @event ModelEvent an event that is triggered before updating a record.
- * You may set [[ModelEvent::isValid]] to be false to stop the update.
- */
- const EVENT_BEFORE_UPDATE = 'beforeUpdate';
- /**
- * @event Event an event that is triggered after a record is updated.
+ * The insert operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional.
*/
- const EVENT_AFTER_UPDATE = 'afterUpdate';
+ const OP_INSERT = 0x01;
/**
- * @event ModelEvent an event that is triggered before deleting a record.
- * You may set [[ModelEvent::isValid]] to be false to stop the deletion.
+ * The update operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional.
*/
- const EVENT_BEFORE_DELETE = 'beforeDelete';
+ const OP_UPDATE = 0x02;
/**
- * @event Event an event that is triggered after a record is deleted.
+ * The delete operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional.
*/
- const EVENT_AFTER_DELETE = 'afterDelete';
-
- /**
- * Represents insert ActiveRecord operation. This constant is used for specifying set of atomic operations
- * for particular scenario in the [[scenarios()]] method.
- */
- const OP_INSERT = 'insert';
+ const OP_DELETE = 0x04;
/**
- * Represents update ActiveRecord operation. This constant is used for specifying set of atomic operations
- * for particular scenario in the [[scenarios()]] method.
+ * All three operations: insert, update, delete.
+ * This is a shortcut of the expression: OP_INSERT | OP_UPDATE | OP_DELETE.
*/
- const OP_UPDATE = 'update';
- /**
- * Represents delete ActiveRecord operation. This constant is used for specifying set of atomic operations
- * for particular scenario in the [[scenarios()]] method.
- */
- const OP_DELETE = 'delete';
-
- /**
- * @var array attribute values indexed by attribute names
- */
- private $_attributes = array();
- /**
- * @var array old attribute values indexed by attribute names.
- */
- private $_oldAttributes;
- /**
- * @var array related models indexed by the relation names
- */
- private $_related;
-
+ const OP_ALL = 0x07;
/**
* Returns the database connection used by this AR class.
@@ -116,41 +53,6 @@ class ActiveRecord extends Model
}
/**
- * Creates an [[ActiveQuery]] instance for query purpose.
- *
- * @include @yii/db/ActiveRecord-find.md
- *
- * @param mixed $q the query parameter. This can be one of the followings:
- *
- * - a scalar value (integer or string): query by a single primary key value and return the
- * corresponding record.
- * - an array of name-value pairs: query by a set of column values and return a single record matching all of them.
- * - null: return a new [[ActiveQuery]] object for further query purpose.
- *
- * @return ActiveQuery|ActiveRecord|null When `$q` is null, a new [[ActiveQuery]] instance
- * is returned; when `$q` is a scalar or an array, an ActiveRecord object matching it will be
- * returned (null will be returned if there is no matching).
- * @throws InvalidConfigException if the AR class does not have a primary key
- * @see createQuery()
- */
- public static function find($q = null)
- {
- $query = static::createQuery();
- if (is_array($q)) {
- return $query->where($q)->one();
- } elseif ($q !== null) {
- // query by primary key
- $primaryKey = static::primaryKey();
- if (isset($primaryKey[0])) {
- return $query->where(array($primaryKey[0] => $q))->one();
- } else {
- throw new InvalidConfigException(get_called_class() . ' must have a primary key.');
- }
- }
- return $query;
- }
-
- /**
* Creates an [[ActiveQuery]] instance with a given SQL statement.
*
* Note that because the SQL statement is already specified, calling additional
@@ -168,7 +70,7 @@ class ActiveRecord extends Model
* @param array $params parameters to be bound to the SQL statement during execution.
* @return ActiveQuery the newly created [[ActiveQuery]] instance
*/
- public static function findBySql($sql, $params = array())
+ public static function findBySql($sql, $params = [])
{
$query = static::createQuery();
$query->sql = $sql;
@@ -180,7 +82,7 @@ class ActiveRecord extends Model
* For example, to change the status to be 1 for all customers whose status is 2:
*
* ~~~
- * Customer::updateAll(array('status' => 1), 'status = 2');
+ * Customer::updateAll(['status' => 1], 'status = 2');
* ~~~
*
* @param array $attributes attribute values (name-value pairs) to be saved into the table
@@ -189,7 +91,7 @@ class ActiveRecord extends Model
* @param array $params the parameters (name => value) to be bound to the query.
* @return integer the number of rows updated
*/
- public static function updateAll($attributes, $condition = '', $params = array())
+ public static function updateAll($attributes, $condition = '', $params = [])
{
$command = static::getDb()->createCommand();
$command->update(static::tableName(), $attributes, $condition, $params);
@@ -201,7 +103,7 @@ class ActiveRecord extends Model
* For example, to increment all customers' age by 1,
*
* ~~~
- * Customer::updateAllCounters(array('age' => 1));
+ * Customer::updateAllCounters(['age' => 1]);
* ~~~
*
* @param array $counters the counters to be updated (attribute name => increment value).
@@ -212,11 +114,11 @@ class ActiveRecord extends Model
* Do not name the parameters as `:bp0`, `:bp1`, etc., because they are used internally by this method.
* @return integer the number of rows updated
*/
- public static function updateAllCounters($counters, $condition = '', $params = array())
+ public static function updateAllCounters($counters, $condition = '', $params = [])
{
$n = 0;
foreach ($counters as $name => $value) {
- $counters[$name] = new Expression("[[$name]]+:bp{$n}", array(":bp{$n}" => $value));
+ $counters[$name] = new Expression("[[$name]]+:bp{$n}", [":bp{$n}" => $value]);
$n++;
}
$command = static::getDb()->createCommand();
@@ -239,7 +141,7 @@ class ActiveRecord extends Model
* @param array $params the parameters (name => value) to be bound to the query.
* @return integer the number of rows deleted
*/
- public static function deleteAll($condition = '', $params = array())
+ public static function deleteAll($condition = '', $params = [])
{
$command = static::getDb()->createCommand();
$command->delete(static::tableName(), $condition, $params);
@@ -248,16 +150,28 @@ class ActiveRecord extends Model
/**
* Creates an [[ActiveQuery]] instance.
- * This method is called by [[find()]], [[findBySql()]] and [[count()]] to start a SELECT query.
+ *
+ * This method is called by [[find()]], [[findBySql()]] to start a SELECT query.
* You may override this method to return a customized query (e.g. `CustomerQuery` specified
* written for querying `Customer` purpose.)
+ *
+ * You may also define default conditions that should apply to all queries unless overridden:
+ *
+ * ```php
+ * public static function createQuery()
+ * {
+ * return parent::createQuery()->where(['deleted' => false]);
+ * }
+ * ```
+ *
+ * Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
+ * default condition. Using [[Query::where()]] will override the default condition.
+ *
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
*/
public static function createQuery()
{
- return new ActiveQuery(array(
- 'modelClass' => get_called_class(),
- ));
+ return new ActiveQuery(['modelClass' => get_called_class()]);
}
/**
@@ -306,349 +220,57 @@ class ActiveRecord extends Model
}
/**
- * Returns the name of the column that stores the lock version for implementing optimistic locking.
- *
- * Optimistic locking allows multiple users to access the same record for edits and avoids
- * potential conflicts. In case when a user attempts to save the record upon some staled data
- * (because another user has modified the data), a [[StaleObjectException]] exception will be thrown,
- * and the update or deletion is skipped.
- *
- * Optimized locking is only supported by [[update()]] and [[delete()]].
- *
- * To use optimized locking:
- *
- * 1. Create a column to store the version number of each row. The column type should be `BIGINT DEFAULT 0`.
- * Override this method to return the name of this column.
- * 2. In the Web form that collects the user input, add a hidden field that stores
- * the lock version of the recording being updated.
- * 3. In the controller action that does the data updating, try to catch the [[StaleObjectException]]
- * and implement necessary business logic (e.g. merging the changes, prompting stated data)
- * to resolve the conflict.
- *
- * @return string the column name that stores the lock version of a table row.
- * If null is returned (default implemented), optimistic locking will not be supported.
- */
- public function optimisticLock()
- {
- return null;
- }
-
- /**
- * PHP getter magic method.
- * This method is overridden so that attributes and related objects can be accessed like properties.
- * @param string $name property name
- * @return mixed property value
- * @see getAttribute
- */
- public function __get($name)
- {
- if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) {
- return $this->_attributes[$name];
- } elseif (isset($this->getTableSchema()->columns[$name])) {
- return null;
- } else {
- $t = strtolower($name);
- if (isset($this->_related[$t]) || $this->_related !== null && array_key_exists($t, $this->_related)) {
- return $this->_related[$t];
- }
- $value = parent::__get($name);
- if ($value instanceof ActiveRelation) {
- return $this->_related[$t] = $value->multiple ? $value->all() : $value->one();
- } else {
- return $value;
- }
- }
- }
-
- /**
- * PHP setter magic method.
- * This method is overridden so that AR attributes can be accessed like properties.
- * @param string $name property name
- * @param mixed $value property value
- */
- public function __set($name, $value)
- {
- if (isset($this->_attributes[$name]) || isset($this->getTableSchema()->columns[$name])) {
- $this->_attributes[$name] = $value;
- } else {
- parent::__set($name, $value);
- }
- }
-
- /**
- * Checks if a property value is null.
- * This method overrides the parent implementation by checking if the named attribute is null or not.
- * @param string $name the property name or the event name
- * @return boolean whether the property value is null
- */
- public function __isset($name)
- {
- try {
- return $this->__get($name) !== null;
- } catch (\Exception $e) {
- return false;
- }
- }
-
- /**
- * Sets a component property to be null.
- * This method overrides the parent implementation by clearing
- * the specified attribute value.
- * @param string $name the property name or the event name
+ * Returns the list of all attribute names of the model.
+ * The default implementation will return all column names of the table associated with this AR class.
+ * @return array list of attribute names.
*/
- public function __unset($name)
+ public function attributes()
{
- if (isset($this->getTableSchema()->columns[$name])) {
- unset($this->_attributes[$name]);
- } else {
- $t = strtolower($name);
- if (isset($this->_related[$t])) {
- unset($this->_related[$t]);
- } else {
- parent::__unset($name);
- }
- }
+ return array_keys(static::getTableSchema()->columns);
}
/**
- * Declares a `has-one` relation.
- * The declaration is returned in terms of an [[ActiveRelation]] instance
- * through which the related record can be queried and retrieved back.
+ * Declares which DB operations should be performed within a transaction in different scenarios.
+ * The supported DB operations are: [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]],
+ * which correspond to the [[insert()]], [[update()]] and [[delete()]] methods, respectively.
+ * By default, these methods are NOT enclosed in a DB transaction.
*
- * A `has-one` relation means that there is at most one related record matching
- * the criteria set by this relation, e.g., a customer has one country.
- *
- * For example, to declare the `country` relation for `Customer` class, we can write
- * the following code in the `Customer` class:
+ * In some scenarios, to ensure data consistency, you may want to enclose some or all of them
+ * in transactions. You can do so by overriding this method and returning the operations
+ * that need to be transactional. For example,
*
* ~~~
- * public function getCountry()
- * {
- * return $this->hasOne('Country', array('id' => 'country_id'));
- * }
- * ~~~
- *
- * Note that in the above, the 'id' key in the `$link` parameter refers to an attribute name
- * in the related class `Country`, while the 'country_id' value refers to an attribute name
- * in the current AR class.
- *
- * Call methods declared in [[ActiveRelation]] to further customize the relation.
- *
- * @param string $class the class name of the related record
- * @param array $link the primary-foreign key constraint. The keys of the array refer to
- * the columns in the table associated with the `$class` model, while the values of the
- * array refer to the corresponding columns in the table associated with this AR class.
- * @return ActiveRelation the relation object.
- */
- public function hasOne($class, $link)
- {
- return new ActiveRelation(array(
- 'modelClass' => $this->getNamespacedClass($class),
- 'primaryModel' => $this,
- 'link' => $link,
- 'multiple' => false,
- ));
- }
-
- /**
- * Declares a `has-many` relation.
- * The declaration is returned in terms of an [[ActiveRelation]] instance
- * through which the related record can be queried and retrieved back.
+ * return [
+ * 'admin' => self::OP_INSERT,
+ * 'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,
+ * // the above is equivalent to the following:
+ * // 'api' => self::OP_ALL,
*
- * A `has-many` relation means that there are multiple related records matching
- * the criteria set by this relation, e.g., a customer has many orders.
- *
- * For example, to declare the `orders` relation for `Customer` class, we can write
- * the following code in the `Customer` class:
- *
- * ~~~
- * public function getOrders()
- * {
- * return $this->hasMany('Order', array('customer_id' => 'id'));
- * }
+ * ];
* ~~~
*
- * Note that in the above, the 'customer_id' key in the `$link` parameter refers to
- * an attribute name in the related class `Order`, while the 'id' value refers to
- * an attribute name in the current AR class.
+ * The above declaration specifies that in the "admin" scenario, the insert operation ([[insert()]])
+ * should be done in a transaction; and in the "api" scenario, all the operations should be done
+ * in a transaction.
*
- * @param string $class the class name of the related record
- * @param array $link the primary-foreign key constraint. The keys of the array refer to
- * the columns in the table associated with the `$class` model, while the values of the
- * array refer to the corresponding columns in the table associated with this AR class.
- * @return ActiveRelation the relation object.
- */
- public function hasMany($class, $link)
- {
- return new ActiveRelation(array(
- 'modelClass' => $this->getNamespacedClass($class),
- 'primaryModel' => $this,
- 'link' => $link,
- 'multiple' => true,
- ));
- }
-
- /**
- * Populates the named relation with the related records.
- * Note that this method does not check if the relation exists or not.
- * @param string $name the relation name (case-insensitive)
- * @param ActiveRecord|array|null the related records to be populated into the relation.
- */
- public function populateRelation($name, $records)
- {
- $this->_related[strtolower($name)] = $records;
- }
-
- /**
- * Returns the list of all attribute names of the model.
- * The default implementation will return all column names of the table associated with this AR class.
- * @return array list of attribute names.
- */
- public function attributes()
- {
- return array_keys($this->getTableSchema()->columns);
- }
-
- /**
- * Returns the named attribute value.
- * If this record is the result of a query and the attribute is not loaded,
- * null will be returned.
- * @param string $name the attribute name
- * @return mixed the attribute value. Null if the attribute is not set or does not exist.
- * @see hasAttribute
- */
- public function getAttribute($name)
- {
- return isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;
- }
-
- /**
- * Sets the named attribute value.
- * @param string $name the attribute name
- * @param mixed $value the attribute value.
- * @see hasAttribute
- */
- public function setAttribute($name, $value)
- {
- $this->_attributes[$name] = $value;
- }
-
- /**
- * Returns the old attribute values.
- * @return array the old attribute values (name-value pairs)
- */
- public function getOldAttributes()
- {
- return $this->_oldAttributes === null ? array() : $this->_oldAttributes;
- }
-
- /**
- * Sets the old attribute values.
- * All existing old attribute values will be discarded.
- * @param array $values old attribute values to be set.
- */
- public function setOldAttributes($values)
- {
- $this->_oldAttributes = $values;
- }
-
- /**
- * Returns the old value of the named attribute.
- * If this record is the result of a query and the attribute is not loaded,
- * null will be returned.
- * @param string $name the attribute name
- * @return mixed the old attribute value. Null if the attribute is not loaded before
- * or does not exist.
- * @see hasAttribute
- */
- public function getOldAttribute($name)
- {
- return isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
- }
-
- /**
- * Sets the old value of the named attribute.
- * @param string $name the attribute name
- * @param mixed $value the old attribute value.
- * @see hasAttribute
- */
- public function setOldAttribute($name, $value)
- {
- $this->_oldAttributes[$name] = $value;
- }
-
- /**
- * Returns a value indicating whether the named attribute has been changed.
- * @param string $name the name of the attribute
- * @return boolean whether the attribute has been changed
+ * @return array the declarations of transactional operations. The array keys are scenarios names,
+ * and the array values are the corresponding transaction operations.
*/
- public function isAttributeChanged($name)
+ public function transactions()
{
- if (isset($this->_attributes[$name], $this->_oldAttributes[$name])) {
- return $this->_attributes[$name] !== $this->_oldAttributes[$name];
- } else {
- return isset($this->_attributes[$name]) || isset($this->_oldAttributes);
- }
+ return [];
}
/**
- * Returns the attribute values that have been modified since they are loaded or saved most recently.
- * @param string[]|null $names the names of the attributes whose values may be returned if they are
- * changed recently. If null, [[attributes()]] will be used.
- * @return array the changed attribute values (name-value pairs)
+ * Creates an [[ActiveRelation]] instance.
+ * This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance.
+ * You may override this method to return a customized relation.
+ * @param array $config the configuration passed to the ActiveRelation class.
+ * @return ActiveRelation the newly created [[ActiveRelation]] instance.
*/
- public function getDirtyAttributes($names = null)
+ public static function createActiveRelation($config = [])
{
- if ($names === null) {
- $names = $this->attributes();
- }
- $names = array_flip($names);
- $attributes = array();
- if ($this->_oldAttributes === null) {
- foreach ($this->_attributes as $name => $value) {
- if (isset($names[$name])) {
- $attributes[$name] = $value;
- }
- }
- } else {
- foreach ($this->_attributes as $name => $value) {
- if (isset($names[$name]) && (!array_key_exists($name, $this->_oldAttributes) || $value !== $this->_oldAttributes[$name])) {
- $attributes[$name] = $value;
- }
- }
- }
- return $attributes;
- }
-
- /**
- * Saves the current record.
- *
- * This method will call [[insert()]] when [[isNewRecord]] is true, or [[update()]]
- * when [[isNewRecord]] is false.
- *
- * For example, to save a customer record:
- *
- * ~~~
- * $customer = new Customer; // or $customer = Customer::find($id);
- * $customer->name = $name;
- * $customer->email = $email;
- * $customer->save();
- * ~~~
- *
- *
- * @param boolean $runValidation whether to perform validation before saving the record.
- * If the validation fails, the record will not be saved to database.
- * @param array $attributes list of attributes that need to be saved. Defaults to null,
- * meaning all attributes that are loaded from DB will be saved.
- * @return boolean whether the saving succeeds
- */
- public function save($runValidation = true, $attributes = null)
- {
- if ($this->getIsNewRecord()) {
- return $this->insert($runValidation, $attributes);
- } else {
- return $this->update($runValidation, $attributes) !== false;
- }
+ return new ActiveRelation($config);
}
/**
@@ -668,7 +290,7 @@ class ActiveRecord extends Model
* [[EVENT_BEFORE_INSERT]], [[EVENT_AFTER_INSERT]] and [[EVENT_AFTER_VALIDATE]]
* will be raised by the corresponding methods.
*
- * Only the [[changedAttributes|changed attribute values]] will be inserted into database.
+ * Only the [[dirtyAttributes|changed attribute values]] will be inserted into database.
*
* If the table's primary key is auto-incremental and is null during insertion,
* it will be populated with the actual value after insertion.
@@ -695,21 +317,21 @@ class ActiveRecord extends Model
return false;
}
$db = static::getDb();
- $transaction = $this->isOperationAtomic(self::OP_INSERT) && $db->getTransaction() === null ? $db->beginTransaction() : null;
- try {
- $result = $this->insertInternal($attributes);
- if ($transaction !== null) {
+ if ($this->isTransactional(self::OP_INSERT) && $db->getTransaction() === null) {
+ $transaction = $db->beginTransaction();
+ try {
+ $result = $this->insertInternal($attributes);
if ($result === false) {
$transaction->rollback();
} else {
$transaction->commit();
}
- }
- } catch (\Exception $e) {
- if ($transaction !== null) {
+ } catch (\Exception $e) {
$transaction->rollback();
+ throw $e;
}
- throw $e;
+ } else {
+ $result = $this->insertInternal($attributes);
}
return $result;
}
@@ -724,8 +346,8 @@ class ActiveRecord extends Model
}
$values = $this->getDirtyAttributes($attributes);
if (empty($values)) {
- foreach ($this->primaryKey() as $key) {
- $values[$key] = isset($this->_attributes[$key]) ? $this->_attributes[$key] : null;
+ foreach ($this->getPrimaryKey(true) as $key => $value) {
+ $values[$key] = $value;
}
}
$db = static::getDb();
@@ -736,14 +358,16 @@ class ActiveRecord extends Model
$table = $this->getTableSchema();
if ($table->sequenceName !== null) {
foreach ($table->primaryKey as $name) {
- if (!isset($this->_attributes[$name])) {
- $this->_oldAttributes[$name] = $this->_attributes[$name] = $db->getLastInsertID($table->sequenceName);
+ if ($this->getAttribute($name) === null) {
+ $id = $db->getLastInsertID($table->sequenceName);
+ $this->setAttribute($name, $id);
+ $this->setOldAttribute($name, $id);
break;
}
}
}
foreach ($values as $name => $value) {
- $this->_oldAttributes[$name] = $value;
+ $this->setOldAttribute($name, $value);
}
$this->afterSave(true);
return true;
@@ -805,89 +429,23 @@ class ActiveRecord extends Model
return false;
}
$db = static::getDb();
- $transaction = $this->isOperationAtomic(self::OP_UPDATE) && $db->getTransaction() === null ? $db->beginTransaction() : null;
- try {
- $result = $this->updateInternal($attributes);
- if ($transaction !== null) {
+ if ($this->isTransactional(self::OP_UPDATE) && $db->getTransaction() === null) {
+ $transaction = $db->beginTransaction();
+ try {
+ $result = $this->updateInternal($attributes);
if ($result === false) {
$transaction->rollback();
} else {
$transaction->commit();
}
- }
- } catch (\Exception $e) {
- if ($transaction !== null) {
+ } catch (\Exception $e) {
$transaction->rollback();
+ throw $e;
}
- throw $e;
- }
- return $result;
- }
-
- /**
- * @see CActiveRecord::update()
- * @throws StaleObjectException
- */
- private function updateInternal($attributes = null)
- {
- if (!$this->beforeSave(false)) {
- return false;
- }
- $values = $this->getDirtyAttributes($attributes);
- if (empty($values)) {
- return 0;
- }
- $condition = $this->getOldPrimaryKey(true);
- $lock = $this->optimisticLock();
- if ($lock !== null) {
- if (!isset($values[$lock])) {
- $values[$lock] = $this->$lock + 1;
- }
- $condition[$lock] = $this->$lock;
- }
- // We do not check the return value of updateAll() because it's possible
- // that the UPDATE statement doesn't change anything and thus returns 0.
- $rows = $this->updateAll($values, $condition);
-
- if ($lock !== null && !$rows) {
- throw new StaleObjectException('The object being updated is outdated.');
- }
-
- foreach ($values as $name => $value) {
- $this->_oldAttributes[$name] = $this->_attributes[$name];
- }
- $this->afterSave(false);
- return $rows;
- }
-
- /**
- * Updates one or several counter columns for the current AR object.
- * Note that this method differs from [[updateAllCounters()]] in that it only
- * saves counters for the current AR object.
- *
- * An example usage is as follows:
- *
- * ~~~
- * $post = Post::find($id);
- * $post->updateCounters(array('view_count' => 1));
- * ~~~
- *
- * @param array $counters the counters to be updated (attribute name => increment value)
- * Use negative values if you want to decrement the counters.
- * @return boolean whether the saving is successful
- * @see updateAllCounters()
- */
- public function updateCounters($counters)
- {
- if ($this->updateAllCounters($counters, $this->getOldPrimaryKey(true)) > 0) {
- foreach ($counters as $name => $value) {
- $this->_attributes[$name] += $value;
- $this->_oldAttributes[$name] = $this->_attributes[$name];
- }
- return true;
} else {
- return false;
+ $result = $this->updateInternal($attributes);
}
+ return $result;
}
/**
@@ -912,7 +470,7 @@ class ActiveRecord extends Model
public function delete()
{
$db = static::getDb();
- $transaction = $this->isOperationAtomic(self::OP_DELETE) && $db->getTransaction() === null ? $db->beginTransaction() : null;
+ $transaction = $this->isTransactional(self::OP_DELETE) && $db->getTransaction() === null ? $db->beginTransaction() : null;
try {
$result = false;
if ($this->beforeDelete()) {
@@ -927,7 +485,7 @@ class ActiveRecord extends Model
if ($lock !== null && !$result) {
throw new StaleObjectException('The object being deleted is outdated.');
}
- $this->_oldAttributes = null;
+ $this->setOldAttributes(null);
$this->afterDelete();
}
if ($transaction !== null) {
@@ -947,505 +505,29 @@ class ActiveRecord extends Model
}
/**
- * Returns a value indicating whether the current record is new.
- * @return boolean whether the record is new and should be inserted when calling [[save()]].
- */
- public function getIsNewRecord()
- {
- return $this->_oldAttributes === null;
- }
-
- /**
- * Initializes the object.
- * This method is called at the end of the constructor.
- * The default implementation will trigger an [[EVENT_INIT]] event.
- * If you override this method, make sure you call the parent implementation at the end
- * to ensure triggering of the event.
- */
- public function init()
- {
- parent::init();
- $this->trigger(self::EVENT_INIT);
- }
-
- /**
- * This method is called when the AR object is created and populated with the query result.
- * The default implementation will trigger an [[EVENT_AFTER_FIND]] event.
- * When overriding this method, make sure you call the parent implementation to ensure the
- * event is triggered.
- */
- public function afterFind()
- {
- $this->trigger(self::EVENT_AFTER_FIND);
- }
-
- /**
- * Sets the value indicating whether the record is new.
- * @param boolean $value whether the record is new and should be inserted when calling [[save()]].
- * @see getIsNewRecord
- */
- public function setIsNewRecord($value)
- {
- $this->_oldAttributes = $value ? null : $this->_attributes;
- }
-
- /**
- * This method is called at the beginning of inserting or updating a record.
- * The default implementation will trigger an [[EVENT_BEFORE_INSERT]] event when `$insert` is true,
- * or an [[EVENT_BEFORE_UPDATE]] event if `$insert` is false.
- * When overriding this method, make sure you call the parent implementation like the following:
- *
- * ~~~
- * public function beforeSave($insert)
- * {
- * if (parent::beforeSave($insert)) {
- * // ...custom code here...
- * return true;
- * } else {
- * return false;
- * }
- * }
- * ~~~
- *
- * @param boolean $insert whether this method called while inserting a record.
- * If false, it means the method is called while updating a record.
- * @return boolean whether the insertion or updating should continue.
- * If false, the insertion or updating will be cancelled.
- */
- public function beforeSave($insert)
- {
- $event = new ModelEvent;
- $this->trigger($insert ? self::EVENT_BEFORE_INSERT : self::EVENT_BEFORE_UPDATE, $event);
- return $event->isValid;
- }
-
- /**
- * This method is called at the end of inserting or updating a record.
- * The default implementation will trigger an [[EVENT_AFTER_INSERT]] event when `$insert` is true,
- * or an [[EVENT_AFTER_UPDATE]] event if `$insert` is false.
- * When overriding this method, make sure you call the parent implementation so that
- * the event is triggered.
- * @param boolean $insert whether this method called while inserting a record.
- * If false, it means the method is called while updating a record.
- */
- public function afterSave($insert)
- {
- $this->trigger($insert ? self::EVENT_AFTER_INSERT : self::EVENT_AFTER_UPDATE);
- }
-
- /**
- * This method is invoked before deleting a record.
- * The default implementation raises the [[EVENT_BEFORE_DELETE]] event.
- * When overriding this method, make sure you call the parent implementation like the following:
- *
- * ~~~
- * public function beforeDelete()
- * {
- * if (parent::beforeDelete()) {
- * // ...custom code here...
- * return true;
- * } else {
- * return false;
- * }
- * }
- * ~~~
- *
- * @return boolean whether the record should be deleted. Defaults to true.
- */
- public function beforeDelete()
- {
- $event = new ModelEvent;
- $this->trigger(self::EVENT_BEFORE_DELETE, $event);
- return $event->isValid;
- }
-
- /**
- * This method is invoked after deleting a record.
- * The default implementation raises the [[EVENT_AFTER_DELETE]] event.
- * You may override this method to do postprocessing after the record is deleted.
- * Make sure you call the parent implementation so that the event is raised properly.
- */
- public function afterDelete()
- {
- $this->trigger(self::EVENT_AFTER_DELETE);
- }
-
- /**
- * Repopulates this active record with the latest data.
- * @param array $attributes
- * @return boolean whether the row still exists in the database. If true, the latest data
- * will be populated to this active record.
- */
- public function refresh($attributes = null)
- {
- $record = $this->find($this->getPrimaryKey(true));
- if ($record === null) {
- return false;
- }
- if ($attributes === null) {
- foreach ($this->attributes() as $name) {
- $this->_attributes[$name] = $record->_attributes[$name];
- }
- $this->_oldAttributes = $this->_attributes;
- } else {
- foreach ($attributes as $name) {
- $this->_oldAttributes[$name] = $this->_attributes[$name] = $record->_attributes[$name];
- }
- }
- return true;
- }
-
- /**
* Returns a value indicating whether the given active record is the same as the current one.
* The comparison is made by comparing the table names and the primary key values of the two active records.
+ * If one of the records [[isNewRecord|is new]] they are also considered not equal.
* @param ActiveRecord $record record to compare to
* @return boolean whether the two active records refer to the same row in the same database table.
*/
public function equals($record)
{
- return $this->tableName() === $record->tableName() && $this->getPrimaryKey() === $record->getPrimaryKey();
- }
-
- /**
- * Returns the primary key value(s).
- * @param boolean $asArray whether to return the primary key value as an array. If true,
- * the return value will be an array with column names as keys and column values as values.
- * Note that for composite primary keys, an array will always be returned regardless of this parameter value.
- * @return mixed the primary key value. An array (column name => column value) is returned if the primary key
- * is composite or `$asArray` is true. A string is returned otherwise (null will be returned if
- * the key value is null).
- */
- public function getPrimaryKey($asArray = false)
- {
- $keys = $this->primaryKey();
- if (count($keys) === 1 && !$asArray) {
- return isset($this->_attributes[$keys[0]]) ? $this->_attributes[$keys[0]] : null;
- } else {
- $values = array();
- foreach ($keys as $name) {
- $values[$name] = isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;
- }
- return $values;
- }
- }
-
- /**
- * Returns the old primary key value(s).
- * This refers to the primary key value that is populated into the record
- * after executing a find method (e.g. find(), findAll()).
- * The value remains unchanged even if the primary key attribute is manually assigned with a different value.
- * @param boolean $asArray whether to return the primary key value as an array. If true,
- * the return value will be an array with column name as key and column value as value.
- * If this is false (default), a scalar value will be returned for non-composite primary key.
- * @return mixed the old primary key value. An array (column name => column value) is returned if the primary key
- * is composite or `$asArray` is true. A string is returned otherwise (null will be returned if
- * the key value is null).
- */
- public function getOldPrimaryKey($asArray = false)
- {
- $keys = $this->primaryKey();
- if (count($keys) === 1 && !$asArray) {
- return isset($this->_oldAttributes[$keys[0]]) ? $this->_oldAttributes[$keys[0]] : null;
- } else {
- $values = array();
- foreach ($keys as $name) {
- $values[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
- }
- return $values;
- }
- }
-
- /**
- * Creates an active record object using a row of data.
- * This method is called by [[ActiveQuery]] to populate the query results
- * into Active Records. It is not meant to be used to create new records.
- * @param array $row attribute values (name => value)
- * @return ActiveRecord the newly created active record.
- */
- public static function create($row)
- {
- $record = static::instantiate($row);
- $columns = static::getTableSchema()->columns;
- foreach ($row as $name => $value) {
- if (isset($columns[$name])) {
- $record->_attributes[$name] = $value;
- } else {
- $record->$name = $value;
- }
- }
- $record->_oldAttributes = $record->_attributes;
- $record->afterFind();
- return $record;
- }
-
- /**
- * Creates an active record instance.
- * This method is called by [[create()]].
- * You may override this method if the instance being created
- * depends on the row data to be populated into the record.
- * For example, by creating a record based on the value of a column,
- * you may implement the so-called single-table inheritance mapping.
- * @param array $row row data to be populated into the record.
- * @return ActiveRecord the newly created active record
- */
- public static function instantiate($row)
- {
- return new static;
- }
-
- /**
- * Returns whether there is an element at the specified offset.
- * This method is required by the interface ArrayAccess.
- * @param mixed $offset the offset to check on
- * @return boolean whether there is an element at the specified offset.
- */
- public function offsetExists($offset)
- {
- return $this->__isset($offset);
- }
-
- /**
- * Returns the relation object with the specified name.
- * A relation is defined by a getter method which returns an [[ActiveRelation]] object.
- * It can be declared in either the Active Record class itself or one of its behaviors.
- * @param string $name the relation name
- * @return ActiveRelation the relation object
- * @throws InvalidParamException if the named relation does not exist.
- */
- public function getRelation($name)
- {
- $getter = 'get' . $name;
- try {
- $relation = $this->$getter();
- if ($relation instanceof ActiveRelation) {
- return $relation;
- }
- } catch (UnknownMethodException $e) {
- throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e);
- }
- }
-
- /**
- * Establishes the relationship between two models.
- *
- * The relationship is established by setting the foreign key value(s) in one model
- * to be the corresponding primary key value(s) in the other model.
- * The model with the foreign key will be saved into database without performing validation.
- *
- * If the relationship involves a pivot table, a new row will be inserted into the
- * pivot table which contains the primary key values from both models.
- *
- * Note that this method requires that the primary key value is not null.
- *
- * @param string $name the name of the relationship
- * @param ActiveRecord $model the model to be linked with the current one.
- * @param array $extraColumns additional column values to be saved into the pivot table.
- * This parameter is only meaningful for a relationship involving a pivot table
- * (i.e., a relation set with `[[ActiveRelation::via()]]` or `[[ActiveRelation::viaTable()]]`.)
- * @throws InvalidCallException if the method is unable to link two models.
- */
- public function link($name, $model, $extraColumns = array())
- {
- $relation = $this->getRelation($name);
-
- if ($relation->via !== null) {
- if (is_array($relation->via)) {
- /** @var $viaRelation ActiveRelation */
- list($viaName, $viaRelation) = $relation->via;
- /** @var $viaClass ActiveRecord */
- $viaClass = $viaRelation->modelClass;
- $viaTable = $viaClass::tableName();
- // unset $viaName so that it can be reloaded to reflect the change
- unset($this->_related[strtolower($viaName)]);
- } else {
- $viaRelation = $relation->via;
- $viaTable = reset($relation->via->from);
- }
- $columns = array();
- foreach ($viaRelation->link as $a => $b) {
- $columns[$a] = $this->$b;
- }
- foreach ($relation->link as $a => $b) {
- $columns[$b] = $model->$a;
- }
- foreach ($extraColumns as $k => $v) {
- $columns[$k] = $v;
- }
- static::getDb()->createCommand()
- ->insert($viaTable, $columns)->execute();
- } else {
- $p1 = $model->isPrimaryKey(array_keys($relation->link));
- $p2 = $this->isPrimaryKey(array_values($relation->link));
- if ($p1 && $p2) {
- if ($this->getIsNewRecord() && $model->getIsNewRecord()) {
- throw new InvalidCallException('Unable to link models: both models are newly created.');
- } elseif ($this->getIsNewRecord()) {
- $this->bindModels(array_flip($relation->link), $this, $model);
- } else {
- $this->bindModels($relation->link, $model, $this);
- }
- } elseif ($p1) {
- $this->bindModels(array_flip($relation->link), $this, $model);
- } elseif ($p2) {
- $this->bindModels($relation->link, $model, $this);
- } else {
- throw new InvalidCallException('Unable to link models: the link does not involve any primary key.');
- }
- }
-
- // update lazily loaded related objects
- if (!$relation->multiple) {
- $this->_related[$name] = $model;
- } elseif (isset($this->_related[$name])) {
- if ($relation->indexBy !== null) {
- $indexBy = $relation->indexBy;
- $this->_related[$name][$model->$indexBy] = $model;
- } else {
- $this->_related[$name][] = $model;
- }
- }
- }
-
- /**
- * Destroys the relationship between two models.
- *
- * The model with the foreign key of the relationship will be deleted if `$delete` is true.
- * Otherwise, the foreign key will be set null and the model will be saved without validation.
- *
- * @param string $name the name of the relationship.
- * @param ActiveRecord $model the model to be unlinked from the current one.
- * @param boolean $delete whether to delete the model that contains the foreign key.
- * If false, the model's foreign key will be set null and saved.
- * If true, the model containing the foreign key will be deleted.
- * @throws InvalidCallException if the models cannot be unlinked
- */
- public function unlink($name, $model, $delete = false)
- {
- $relation = $this->getRelation($name);
-
- if ($relation->via !== null) {
- if (is_array($relation->via)) {
- /** @var $viaRelation ActiveRelation */
- list($viaName, $viaRelation) = $relation->via;
- /** @var $viaClass ActiveRecord */
- $viaClass = $viaRelation->modelClass;
- $viaTable = $viaClass::tableName();
- unset($this->_related[strtolower($viaName)]);
- } else {
- $viaRelation = $relation->via;
- $viaTable = reset($relation->via->from);
- }
- $columns = array();
- foreach ($viaRelation->link as $a => $b) {
- $columns[$a] = $this->$b;
- }
- foreach ($relation->link as $a => $b) {
- $columns[$b] = $model->$a;
- }
- $command = static::getDb()->createCommand();
- if ($delete) {
- $command->delete($viaTable, $columns)->execute();
- } else {
- $nulls = array();
- foreach (array_keys($columns) as $a) {
- $nulls[$a] = null;
- }
- $command->update($viaTable, $nulls, $columns)->execute();
- }
- } else {
- $p1 = $model->isPrimaryKey(array_keys($relation->link));
- $p2 = $this->isPrimaryKey(array_values($relation->link));
- if ($p1 && $p2 || $p2) {
- foreach ($relation->link as $a => $b) {
- $model->$a = null;
- }
- $delete ? $model->delete() : $model->save(false);
- } elseif ($p1) {
- foreach ($relation->link as $b) {
- $this->$b = null;
- }
- $delete ? $this->delete() : $this->save(false);
- } else {
- throw new InvalidCallException('Unable to unlink models: the link does not involve any primary key.');
- }
- }
-
- if (!$relation->multiple) {
- unset($this->_related[$name]);
- } elseif (isset($this->_related[$name])) {
- /** @var $b ActiveRecord */
- foreach ($this->_related[$name] as $a => $b) {
- if ($model->getPrimaryKey() == $b->getPrimaryKey()) {
- unset($this->_related[$name][$a]);
- }
- }
- }
- }
-
- /**
- * Changes the given class name into a namespaced one.
- * If the given class name is already namespaced, no change will be made.
- * Otherwise, the class name will be changed to use the same namespace as
- * the current AR class.
- * @param string $class the class name to be namespaced
- * @return string the namespaced class name
- */
- protected function getNamespacedClass($class)
- {
- if (strpos($class, '\\') === false) {
- $primaryClass = get_class($this);
- if (($pos = strrpos($primaryClass, '\\')) !== false) {
- return substr($primaryClass, 0, $pos + 1) . $class;
- }
- }
- return $class;
- }
-
- /**
- * @param array $link
- * @param ActiveRecord $foreignModel
- * @param ActiveRecord $primaryModel
- * @throws InvalidCallException
- */
- private function bindModels($link, $foreignModel, $primaryModel)
- {
- foreach ($link as $fk => $pk) {
- $value = $primaryModel->$pk;
- if ($value === null) {
- throw new InvalidCallException('Unable to link models: the primary key of ' . get_class($primaryModel) . ' is null.');
- }
- $foreignModel->$fk = $value;
- }
- $foreignModel->save(false);
- }
-
- /**
- * @param array $keys
- * @return boolean
- */
- private function isPrimaryKey($keys)
- {
- $pks = $this->primaryKey();
- foreach ($keys as $key) {
- if (!in_array($key, $pks, true)) {
- return false;
- }
+ if ($this->isNewRecord || $record->isNewRecord) {
+ return false;
}
- return true;
+ return $this->tableName() === $record->tableName() && $this->getPrimaryKey() === $record->getPrimaryKey();
}
/**
- * @param string $operation possible values are ActiveRecord::INSERT, ActiveRecord::UPDATE and ActiveRecord::DELETE.
- * @return boolean whether given operation is atomic. Currently active scenario is taken into account.
+ * Returns a value indicating whether the specified operation is transactional in the current [[scenario]].
+ * @param integer $operation the operation to check. Possible values are [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]].
+ * @return boolean whether the specified operation is transactional in the current [[scenario]].
*/
- private function isOperationAtomic($operation)
+ public function isTransactional($operation)
{
$scenario = $this->getScenario();
- $scenarios = $this->scenarios();
- if (isset($scenarios[$scenario], $scenario[$scenario]['atomic']) && is_array($scenarios[$scenario]['atomic'])) {
- return in_array($operation, $scenarios[$scenario]['atomic']);
- } else {
- return false;
- }
+ $transactions = $this->transactions();
+ return isset($transactions[$scenario]) && ($transactions[$scenario] & $operation);
}
}
diff --git a/framework/yii/db/ActiveRecordInterface.php b/framework/yii/db/ActiveRecordInterface.php
new file mode 100644
index 0000000..73db852
--- /dev/null
+++ b/framework/yii/db/ActiveRecordInterface.php
@@ -0,0 +1,310 @@
+
+ */
+
+namespace yii\db;
+
+/**
+ * ActiveRecordInterface
+ *
+ * @author Qiang Xue
+ * @author Carsten Brandt
+ * @since 2.0
+ */
+interface ActiveRecordInterface
+{
+ /**
+ * Returns the primary key **name(s)** for this AR class.
+ *
+ * Note that an array should be returned even when the record only has a single primary key.
+ *
+ * For the primary key **value** see [[getPrimaryKey()]] instead.
+ *
+ * @return string[] the primary key name(s) for this AR class.
+ */
+ public static function primaryKey();
+
+ /**
+ * Returns the list of all attribute names of the record.
+ * @return array list of attribute names.
+ */
+ public function attributes();
+
+ /**
+ * Returns the named attribute value.
+ * If this record is the result of a query and the attribute is not loaded,
+ * null will be returned.
+ * @param string $name the attribute name
+ * @return mixed the attribute value. Null if the attribute is not set or does not exist.
+ * @see hasAttribute()
+ */
+ public function getAttribute($name);
+
+ /**
+ * Sets the named attribute value.
+ * @param string $name the attribute name.
+ * @param mixed $value the attribute value.
+ * @see hasAttribute()
+ */
+ public function setAttribute($name, $value);
+
+ /**
+ * Returns a value indicating whether the record has an attribute with the specified name.
+ * @param string $name the name of the attribute
+ * @return boolean whether the record has an attribute with the specified name.
+ */
+ public function hasAttribute($name);
+
+ /**
+ * Returns the primary key value(s).
+ * @param boolean $asArray whether to return the primary key value as an array. If true,
+ * the return value will be an array with attribute names as keys and attribute values as values.
+ * Note that for composite primary keys, an array will always be returned regardless of this parameter value.
+ * @return mixed the primary key value. An array (attribute name => attribute value) is returned if the primary key
+ * is composite or `$asArray` is true. A string is returned otherwise (null will be returned if
+ * the key value is null).
+ */
+ public function getPrimaryKey($asArray = false);
+
+ /**
+ * Returns the old primary key value(s).
+ * This refers to the primary key value that is populated into the record
+ * after executing a find method (e.g. find(), findAll()).
+ * The value remains unchanged even if the primary key attribute is manually assigned with a different value.
+ * @param boolean $asArray whether to return the primary key value as an array. If true,
+ * the return value will be an array with column name as key and column value as value.
+ * If this is false (default), a scalar value will be returned for non-composite primary key.
+ * @property mixed The old primary key value. An array (column name => column value) is
+ * returned if the primary key is composite. A string is returned otherwise (null will be
+ * returned if the key value is null).
+ * @return mixed the old primary key value. An array (column name => column value) is returned if the primary key
+ * is composite or `$asArray` is true. A string is returned otherwise (null will be returned if
+ * the key value is null).
+ */
+ public function getOldPrimaryKey($asArray = false);
+
+ /**
+ * Creates an [[ActiveQueryInterface|ActiveQuery]] instance for query purpose.
+ *
+ * This method is usually ment to be used like this:
+ *
+ * ```php
+ * Customer::find(1); // find one customer by primary key
+ * Customer::find()->all(); // find all customers
+ * ```
+ *
+ * @param mixed $q the query parameter. This can be one of the followings:
+ *
+ * - a scalar value (integer or string): query by a single primary key value and return the
+ * corresponding record.
+ * - an array of name-value pairs: query by a set of attribute values and return a single record matching all of them.
+ * - null (not specified): return a new [[ActiveQuery]] object for further query purpose.
+ *
+ * @return ActiveQueryInterface|static|null When `$q` is null, a new [[ActiveQuery]] instance
+ * is returned; when `$q` is a scalar or an array, an ActiveRecord object matching it will be
+ * returned (null will be returned if there is no matching).
+ */
+ public static function find($q = null);
+
+ /**
+ * Creates an [[ActiveQueryInterface|ActiveQuery]] instance.
+ *
+ * This method is called by [[find()]] to start a SELECT query.
+ * You may override this method to return a customized query (e.g. `CustomerQuery` specified
+ * written for querying `Customer` purpose.)
+ *
+ * You may also define default conditions that should apply to all queries unless overridden:
+ *
+ * ```php
+ * public static function createQuery()
+ * {
+ * return parent::createQuery()->where(['deleted' => false]);
+ * }
+ * ```
+ *
+ * Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
+ * default condition. Using [[Query::where()]] will override the default condition.
+ *
+ * @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.
+ */
+ public static function createQuery();
+
+ /**
+ * Updates records using the provided attribute values and conditions.
+ * For example, to change the status to be 1 for all customers whose status is 2:
+ *
+ * ~~~
+ * Customer::updateAll(['status' => 1], ['status' => '2']);
+ * ~~~
+ *
+ * @param array $attributes attribute values (name-value pairs) to be saved for the record.
+ * Unlike [[update()]] these are not going to be validated.
+ * @param array $condition the condition that matches the records that should get updated.
+ * Please refer to [[QueryInterface::where()]] on how to specify this parameter.
+ * An empty condition will match all records.
+ * @return integer the number of rows updated
+ */
+ public static function updateAll($attributes, $condition = null);
+
+ /**
+ * Deletes records using the provided conditions.
+ * WARNING: If you do not specify any condition, this method will delete ALL rows in the table.
+ *
+ * For example, to delete all customers whose status is 3:
+ *
+ * ~~~
+ * Customer::deleteAll([status = 3]);
+ * ~~~
+ *
+ * @param array $condition the condition that matches the records that should get deleted.
+ * Please refer to [[QueryInterface::where()]] on how to specify this parameter.
+ * An empty condition will match all records.
+ * @return integer the number of rows deleted
+ */
+ public static function deleteAll($condition = null);
+
+ /**
+ * Saves the current record.
+ *
+ * This method will call [[insert()]] when [[isNewRecord]] is true, or [[update()]]
+ * when [[isNewRecord]] is false.
+ *
+ * For example, to save a customer record:
+ *
+ * ~~~
+ * $customer = new Customer; // or $customer = Customer::find($id);
+ * $customer->name = $name;
+ * $customer->email = $email;
+ * $customer->save();
+ * ~~~
+ *
+ * @param boolean $runValidation whether to perform validation before saving the record.
+ * If the validation fails, the record will not be saved to database. `false` will be returned
+ * in this case.
+ * @param array $attributes list of attributes that need to be saved. Defaults to null,
+ * meaning all attributes that are loaded from DB will be saved.
+ * @return boolean whether the saving succeeds
+ */
+ public function save($runValidation = true, $attributes = null);
+
+ /**
+ * Inserts the record into the database using the attribute values of this record.
+ *
+ * Usage example:
+ *
+ * ```php
+ * $customer = new Customer;
+ * $customer->name = $name;
+ * $customer->email = $email;
+ * $customer->insert();
+ * ```
+ *
+ * @param boolean $runValidation whether to perform validation before saving the record.
+ * If the validation fails, the record will not be inserted into the database.
+ * @param array $attributes list of attributes that need to be saved. Defaults to null,
+ * meaning all attributes that are loaded from DB will be saved.
+ * @return boolean whether the attributes are valid and the record is inserted successfully.
+ */
+ public function insert($runValidation = true, $attributes = null);
+
+ /**
+ * Saves the changes to this active record into the database.
+ *
+ * Usage example:
+ *
+ * ```php
+ * $customer = Customer::find($id);
+ * $customer->name = $name;
+ * $customer->email = $email;
+ * $customer->update();
+ * ```
+ *
+ * @param boolean $runValidation whether to perform validation before saving the record.
+ * If the validation fails, the record will not be inserted into the database.
+ * @param array $attributes list of attributes that need to be saved. Defaults to null,
+ * meaning all attributes that are loaded from DB will be saved.
+ * @return integer|boolean the number of rows affected, or false if validation fails
+ * or updating process is stopped for other reasons.
+ * Note that it is possible that the number of rows affected is 0, even though the
+ * update execution is successful.
+ */
+ public function update($runValidation = true, $attributes = null);
+
+ /**
+ * Deletes the record from the database.
+ *
+ * @return integer|boolean the number of rows deleted, or false if the deletion is unsuccessful for some reason.
+ * Note that it is possible that the number of rows deleted is 0, even though the deletion execution is successful.
+ */
+ public function delete();
+
+ /**
+ * Returns a value indicating whether the current record is new (not saved in the database).
+ * @return boolean whether the record is new and should be inserted when calling [[save()]].
+ */
+ public function getIsNewRecord();
+
+ /**
+ * Returns a value indicating whether the given active record is the same as the current one.
+ * Two [[isNewRecord|new]] records are considered to be not equal.
+ * @param static $record record to compare to
+ * @return boolean whether the two active records refer to the same row in the same database table.
+ */
+ public function equals($record);
+
+ /**
+ * Creates an [[ActiveRelationInterface|ActiveRelation]] instance.
+ * This method is called by [[BaseActiveRecord::hasOne()]] and [[BaseActiveRecord::hasMany()]] to
+ * create a relation instance.
+ * You may override this method to return a customized relation.
+ * @param array $config the configuration passed to the ActiveRelation class.
+ * @return ActiveRelation the newly created [[ActiveRelation]] instance.
+ */
+ public static function createActiveRelation($config = []);
+
+ /**
+ * Returns the relation object with the specified name.
+ * A relation is defined by a getter method which returns an [[ActiveRelationInterface|ActiveRelation]] object.
+ * It can be declared in either the ActiveRecord class itself or one of its behaviors.
+ * @param string $name the relation name
+ * @return ActiveRelation the relation object
+ */
+ public function getRelation($name);
+
+ /**
+ * Establishes the relationship between two records.
+ *
+ * The relationship is established by setting the foreign key value(s) in one record
+ * to be the corresponding primary key value(s) in the other record.
+ * The record with the foreign key will be saved into database without performing validation.
+ *
+ * If the relationship involves a pivot table, a new row will be inserted into the
+ * pivot table which contains the primary key values from both records.
+ *
+ * This method requires that the primary key value is not null.
+ *
+ * @param string $name the case sensitive name of the relationship.
+ * @param static $model the record to be linked with the current one.
+ * @param array $extraColumns additional column values to be saved into the pivot table.
+ * This parameter is only meaningful for a relationship involving a pivot table
+ * (i.e., a relation set with `[[ActiveRelationInterface::via()]]`.)
+ */
+ public function link($name, $model, $extraColumns = []);
+
+ /**
+ * Destroys the relationship between two records.
+ *
+ * The record with the foreign key of the relationship will be deleted if `$delete` is true.
+ * Otherwise, the foreign key will be set null and the record will be saved without validation.
+ *
+ * @param string $name the case sensitive name of the relationship.
+ * @param static $model the model to be unlinked from the current one.
+ * @param boolean $delete whether to delete the model that contains the foreign key.
+ * If false, the model's foreign key will be set null and saved.
+ * If true, the model containing the foreign key will be deleted.
+ */
+ public function unlink($name, $model, $delete = false);
+}
diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php
index 42ae0e7..0659ee3 100644
--- a/framework/yii/db/ActiveRelation.php
+++ b/framework/yii/db/ActiveRelation.php
@@ -8,10 +8,6 @@
namespace yii\db;
-use yii\db\Connection;
-use yii\db\Command;
-use yii\base\InvalidConfigException;
-
/**
* ActiveRelation represents a relation between two Active Record classes.
*
@@ -25,49 +21,12 @@ use yii\base\InvalidConfigException;
* If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method.
*
* @author Qiang Xue
+ * @author Carsten Brandt
* @since 2.0
*/
-class ActiveRelation extends ActiveQuery
+class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
{
- /**
- * @var boolean whether this relation should populate all query results into AR instances.
- * If false, only the first row of the results will be retrieved.
- */
- public $multiple;
- /**
- * @var ActiveRecord the primary model that this relation is associated with.
- * This is used only in lazy loading with dynamic query options.
- */
- public $primaryModel;
- /**
- * @var array the columns of the primary and foreign tables that establish the relation.
- * The array keys must be columns of the table for this relation, and the array values
- * must be the corresponding columns from the primary table.
- * Do not prefix or quote the column names as they will be done automatically by Yii.
- */
- public $link;
- /**
- * @var array|ActiveRelation the query associated with the pivot table. Please call [[via()]]
- * or [[viaTable()]] to set this property instead of directly setting it.
- */
- public $via;
-
- /**
- * Specifies the relation associated with the pivot table.
- * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
- * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
- * Its signature should be `function($query)`, where `$query` is the query to be customized.
- * @return ActiveRelation the relation object itself.
- */
- public function via($relationName, $callable = null)
- {
- $relation = $this->primaryModel->getRelation($relationName);
- $this->via = array($relationName, $relation);
- if ($callable !== null) {
- call_user_func($callable, $relation);
- }
- return $this;
- }
+ use ActiveRelationTrait;
/**
* Specifies the pivot table.
@@ -77,17 +36,17 @@ class ActiveRelation extends ActiveQuery
* in the [[primaryModel]] table.
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
- * @return ActiveRelation
+ * @return static
*/
public function viaTable($tableName, $link, $callable = null)
{
- $relation = new ActiveRelation(array(
+ $relation = new ActiveRelation([
'modelClass' => get_class($this->primaryModel),
- 'from' => array($tableName),
+ 'from' => [$tableName],
'link' => $link,
'multiple' => true,
'asArray' => true,
- ));
+ ]);
$this->via = $relation;
if ($callable !== null) {
call_user_func($callable, $relation);
@@ -104,14 +63,15 @@ class ActiveRelation extends ActiveQuery
public function createCommand($db = null)
{
if ($this->primaryModel !== null) {
+ $where = $this->where;
// lazy loading
if ($this->via instanceof self) {
// via pivot table
- $viaModels = $this->via->findPivotRows(array($this->primaryModel));
+ $viaModels = $this->via->findPivotRows([$this->primaryModel]);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
- /** @var $viaQuery ActiveRelation */
+ /** @var ActiveRelation $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
@@ -119,186 +79,16 @@ class ActiveRelation extends ActiveQuery
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
- $viaModels = $model === null ? array() : array($model);
+ $viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
- $this->filterByModels(array($this->primaryModel));
+ $this->filterByModels([$this->primaryModel]);
}
+ $command = parent::createCommand($db);
+ $this->where = $where;
+ return $command;
}
return parent::createCommand($db);
}
-
- /**
- * Finds the related records and populates them into the primary models.
- * This method is internally used by [[ActiveQuery]]. Do not call it directly.
- * @param string $name the relation name
- * @param array $primaryModels primary models
- * @return array the related models
- * @throws InvalidConfigException
- */
- public function findWith($name, &$primaryModels)
- {
- if (!is_array($this->link)) {
- throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.');
- }
-
- if ($this->via instanceof self) {
- // via pivot table
- /** @var $viaQuery ActiveRelation */
- $viaQuery = $this->via;
- $viaModels = $viaQuery->findPivotRows($primaryModels);
- $this->filterByModels($viaModels);
- } elseif (is_array($this->via)) {
- // via relation
- /** @var $viaQuery ActiveRelation */
- list($viaName, $viaQuery) = $this->via;
- $viaQuery->primaryModel = null;
- $viaModels = $viaQuery->findWith($viaName, $primaryModels);
- $this->filterByModels($viaModels);
- } else {
- $this->filterByModels($primaryModels);
- }
-
- if (count($primaryModels) === 1 && !$this->multiple) {
- $model = $this->one();
- foreach ($primaryModels as $i => $primaryModel) {
- if ($primaryModel instanceof ActiveRecord) {
- $primaryModel->populateRelation($name, $model);
- } else {
- $primaryModels[$i][$name] = $model;
- }
- }
- return array($model);
- } else {
- $models = $this->all();
- if (isset($viaModels, $viaQuery)) {
- $buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link);
- } else {
- $buckets = $this->buildBuckets($models, $this->link);
- }
-
- $link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link);
- foreach ($primaryModels as $i => $primaryModel) {
- $key = $this->getModelKey($primaryModel, $link);
- $value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? array() : null);
- if ($primaryModel instanceof ActiveRecord) {
- $primaryModel->populateRelation($name, $value);
- } else {
- $primaryModels[$i][$name] = $value;
- }
- }
- return $models;
- }
- }
-
- /**
- * @param array $models
- * @param array $link
- * @param array $viaModels
- * @param array $viaLink
- * @return array
- */
- private function buildBuckets($models, $link, $viaModels = null, $viaLink = null)
- {
- $buckets = array();
- $linkKeys = array_keys($link);
- foreach ($models as $i => $model) {
- $key = $this->getModelKey($model, $linkKeys);
- if ($this->indexBy !== null) {
- $buckets[$key][$i] = $model;
- } else {
- $buckets[$key][] = $model;
- }
- }
-
- if ($viaModels !== null) {
- $viaBuckets = array();
- $viaLinkKeys = array_keys($viaLink);
- $linkValues = array_values($link);
- foreach ($viaModels as $viaModel) {
- $key1 = $this->getModelKey($viaModel, $viaLinkKeys);
- $key2 = $this->getModelKey($viaModel, $linkValues);
- if (isset($buckets[$key2])) {
- foreach ($buckets[$key2] as $i => $bucket) {
- if ($this->indexBy !== null) {
- $viaBuckets[$key1][$i] = $bucket;
- } else {
- $viaBuckets[$key1][] = $bucket;
- }
- }
- }
- }
- $buckets = $viaBuckets;
- }
-
- if (!$this->multiple) {
- foreach ($buckets as $i => $bucket) {
- $buckets[$i] = reset($bucket);
- }
- }
- return $buckets;
- }
-
- /**
- * @param ActiveRecord|array $model
- * @param array $attributes
- * @return string
- */
- private function getModelKey($model, $attributes)
- {
- if (count($attributes) > 1) {
- $key = array();
- foreach ($attributes as $attribute) {
- $key[] = $model[$attribute];
- }
- return serialize($key);
- } else {
- $attribute = reset($attributes);
- return $model[$attribute];
- }
- }
-
- /**
- * @param array $models
- */
- private function filterByModels($models)
- {
- $attributes = array_keys($this->link);
- $values = array();
- if (count($attributes) === 1) {
- // single key
- $attribute = reset($this->link);
- foreach ($models as $model) {
- $values[] = $model[$attribute];
- }
- } else {
- // composite keys
- foreach ($models as $model) {
- $v = array();
- foreach ($this->link as $attribute => $link) {
- $v[$attribute] = $model[$link];
- }
- $values[] = $v;
- }
- }
- $this->andWhere(array('in', $attributes, array_unique($values, SORT_REGULAR)));
- }
-
- /**
- * @param ActiveRecord[] $primaryModels
- * @return array
- */
- private function findPivotRows($primaryModels)
- {
- if (empty($primaryModels)) {
- return array();
- }
- $this->filterByModels($primaryModels);
- /** @var $primaryModel ActiveRecord */
- $primaryModel = reset($primaryModels);
- $db = $primaryModel->getDb();
- $sql = $db->getQueryBuilder()->build($this);
- return $db->createCommand($sql, $this->params)->queryAll();
- }
}
diff --git a/framework/yii/db/ActiveRelationInterface.php b/framework/yii/db/ActiveRelationInterface.php
new file mode 100644
index 0000000..84e0648
--- /dev/null
+++ b/framework/yii/db/ActiveRelationInterface.php
@@ -0,0 +1,29 @@
+
+ * @author Carsten Brandt
+ * @since 2.0
+ */
+interface ActiveRelationInterface extends ActiveQueryInterface
+{
+ /**
+ * Specifies the relation associated with the pivot table.
+ * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
+ * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
+ * Its signature should be `function($query)`, where `$query` is the query to be customized.
+ * @return static the relation object itself.
+ */
+ public function via($relationName, $callable = null);
+}
diff --git a/framework/yii/db/ActiveRelationTrait.php b/framework/yii/db/ActiveRelationTrait.php
new file mode 100644
index 0000000..dac3028
--- /dev/null
+++ b/framework/yii/db/ActiveRelationTrait.php
@@ -0,0 +1,257 @@
+
+ * @author Carsten Brandt
+ * @since 2.0
+ */
+trait ActiveRelationTrait
+{
+ /**
+ * @var boolean whether this relation should populate all query results into AR instances.
+ * If false, only the first row of the results will be retrieved.
+ */
+ public $multiple;
+ /**
+ * @var ActiveRecord the primary model that this relation is associated with.
+ * This is used only in lazy loading with dynamic query options.
+ */
+ public $primaryModel;
+ /**
+ * @var array the columns of the primary and foreign tables that establish the relation.
+ * The array keys must be columns of the table for this relation, and the array values
+ * must be the corresponding columns from the primary table.
+ * Do not prefix or quote the column names as this will be done automatically by Yii.
+ */
+ public $link;
+ /**
+ * @var array the query associated with the pivot table. Please call [[via()]]
+ * to set this property instead of directly setting it.
+ */
+ public $via;
+
+ /**
+ * Clones internal objects.
+ */
+ public function __clone()
+ {
+ // make a clone of "via" object so that the same query object can be reused multiple times
+ if (is_object($this->via)) {
+ $this->via = clone $this->via;
+ } elseif (is_array($this->via)) {
+ $this->via = [$this->via[0], clone $this->via[1]];
+ }
+ }
+
+ /**
+ * Specifies the relation associated with the pivot table.
+ * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
+ * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
+ * Its signature should be `function($query)`, where `$query` is the query to be customized.
+ * @return static the relation object itself.
+ */
+ public function via($relationName, $callable = null)
+ {
+ $relation = $this->primaryModel->getRelation($relationName);
+ $this->via = [$relationName, $relation];
+ if ($callable !== null) {
+ call_user_func($callable, $relation);
+ }
+ return $this;
+ }
+
+ /**
+ * Finds the related records and populates them into the primary models.
+ * @param string $name the relation name
+ * @param array $primaryModels primary models
+ * @return array the related models
+ * @throws InvalidConfigException if [[link]] is invalid
+ */
+ public function populateRelation($name, &$primaryModels)
+ {
+ if (!is_array($this->link)) {
+ throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.');
+ }
+
+ if ($this->via instanceof self) {
+ // via pivot table
+ /** @var ActiveRelationTrait $viaQuery */
+ $viaQuery = $this->via;
+ $viaModels = $viaQuery->findPivotRows($primaryModels);
+ $this->filterByModels($viaModels);
+ } elseif (is_array($this->via)) {
+ // via relation
+ /** @var ActiveRelationTrait $viaQuery */
+ list($viaName, $viaQuery) = $this->via;
+ $viaQuery->primaryModel = null;
+ $viaModels = $viaQuery->populateRelation($viaName, $primaryModels);
+ $this->filterByModels($viaModels);
+ } else {
+ $this->filterByModels($primaryModels);
+ }
+
+ if (count($primaryModels) === 1 && !$this->multiple) {
+ $model = $this->one();
+ foreach ($primaryModels as $i => $primaryModel) {
+ if ($primaryModel instanceof ActiveRecordInterface) {
+ $primaryModel->populateRelation($name, $model);
+ } else {
+ $primaryModels[$i][$name] = $model;
+ }
+ }
+ return [$model];
+ } else {
+ $models = $this->all();
+ if (isset($viaModels, $viaQuery)) {
+ $buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link);
+ } else {
+ $buckets = $this->buildBuckets($models, $this->link);
+ }
+
+ $link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link);
+ foreach ($primaryModels as $i => $primaryModel) {
+ $key = $this->getModelKey($primaryModel, $link);
+ $value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null);
+ if ($primaryModel instanceof ActiveRecordInterface) {
+ $primaryModel->populateRelation($name, $value);
+ } else {
+ $primaryModels[$i][$name] = $value;
+ }
+ }
+ return $models;
+ }
+ }
+
+ /**
+ * @param array $models
+ * @param array $link
+ * @param array $viaModels
+ * @param array $viaLink
+ * @return array
+ */
+ private function buildBuckets($models, $link, $viaModels = null, $viaLink = null)
+ {
+ if ($viaModels !== null) {
+ $map = [];
+ $viaLinkKeys = array_keys($viaLink);
+ $linkValues = array_values($link);
+ foreach ($viaModels as $viaModel) {
+ $key1 = $this->getModelKey($viaModel, $viaLinkKeys);
+ $key2 = $this->getModelKey($viaModel, $linkValues);
+ $map[$key2][$key1] = true;
+ }
+ }
+
+ $buckets = [];
+ $linkKeys = array_keys($link);
+
+ if (isset($map)) {
+ foreach ($models as $i => $model) {
+ $key = $this->getModelKey($model, $linkKeys);
+ if (isset($map[$key])) {
+ foreach (array_keys($map[$key]) as $key2) {
+ if ($this->indexBy !== null) {
+ $buckets[$key2][$i] = $model;
+ } else {
+ $buckets[$key2][] = $model;
+ }
+ }
+ }
+ }
+ } else {
+ foreach ($models as $i => $model) {
+ $key = $this->getModelKey($model, $linkKeys);
+ if ($this->indexBy !== null) {
+ $buckets[$key][$i] = $model;
+ } else {
+ $buckets[$key][] = $model;
+ }
+ }
+ }
+
+ if (!$this->multiple) {
+ foreach ($buckets as $i => $bucket) {
+ $buckets[$i] = reset($bucket);
+ }
+ }
+ return $buckets;
+ }
+
+ /**
+ * @param array $models
+ */
+ private function filterByModels($models)
+ {
+ $attributes = array_keys($this->link);
+ $values = [];
+ if (count($attributes) === 1) {
+ // single key
+ $attribute = reset($this->link);
+ foreach ($models as $model) {
+ if (($value = $model[$attribute]) !== null) {
+ $values[] = $value;
+ }
+ }
+ } else {
+ // composite keys
+ foreach ($models as $model) {
+ $v = [];
+ foreach ($this->link as $attribute => $link) {
+ $v[$attribute] = $model[$link];
+ }
+ $values[] = $v;
+ }
+ }
+ $this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]);
+ }
+
+ /**
+ * @param ActiveRecord|array $model
+ * @param array $attributes
+ * @return string
+ */
+ private function getModelKey($model, $attributes)
+ {
+ if (count($attributes) > 1) {
+ $key = [];
+ foreach ($attributes as $attribute) {
+ $key[] = $model[$attribute];
+ }
+ return serialize($key);
+ } else {
+ $attribute = reset($attributes);
+ $key = $model[$attribute];
+ return is_scalar($key) ? $key : serialize($key);
+ }
+ }
+
+ /**
+ * @param array $primaryModels either array of AR instances or arrays
+ * @return array
+ */
+ private function findPivotRows($primaryModels)
+ {
+ if (empty($primaryModels)) {
+ return [];
+ }
+ $this->filterByModels($primaryModels);
+ /** @var ActiveRecord $primaryModel */
+ $primaryModel = reset($primaryModels);
+ if (!$primaryModel instanceof ActiveRecordInterface) {
+ // when primaryModels are array of arrays (asArray case)
+ $primaryModel = new $this->modelClass;
+ }
+ return $this->asArray()->all($primaryModel->getDb());
+ }
+}
diff --git a/framework/yii/db/BaseActiveRecord.php b/framework/yii/db/BaseActiveRecord.php
new file mode 100644
index 0000000..e20501b
--- /dev/null
+++ b/framework/yii/db/BaseActiveRecord.php
@@ -0,0 +1,1243 @@
+
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\db;
+
+use yii\base\InvalidConfigException;
+use yii\base\Model;
+use yii\base\InvalidParamException;
+use yii\base\ModelEvent;
+use yii\base\NotSupportedException;
+use yii\base\UnknownMethodException;
+use yii\base\InvalidCallException;
+use yii\helpers\StringHelper;
+use yii\helpers\Inflector;
+
+/**
+ * ActiveRecord is the base class for classes representing relational data in terms of objects.
+ *
+ * @include @yii/db/ActiveRecord.md
+ *
+ * @property array $dirtyAttributes The changed attribute values (name-value pairs). This property is
+ * read-only.
+ * @property boolean $isNewRecord Whether the record is new and should be inserted when calling [[save()]].
+ * @property array $oldAttributes The old attribute values (name-value pairs).
+ * @property mixed $oldPrimaryKey The old primary key value. An array (column name => column value) is
+ * returned if the primary key is composite. A string is returned otherwise (null will be returned if the key
+ * value is null). This property is read-only.
+ * @property array $relatedRecords An array of the populated related records indexed by relation names. This property is
+ * read-only.
+ * @property mixed $primaryKey The primary key value. An array (column name => column value) is returned if
+ * the primary key is composite. A string is returned otherwise (null will be returned if the key value is null).
+ * This property is read-only.
+ *
+ * @author Qiang Xue
+ * @author Carsten Brandt
+ * @since 2.0
+ */
+abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
+{
+ /**
+ * @event Event an event that is triggered when the record is initialized via [[init()]].
+ */
+ const EVENT_INIT = 'init';
+ /**
+ * @event Event an event that is triggered after the record is created and populated with query result.
+ */
+ const EVENT_AFTER_FIND = 'afterFind';
+ /**
+ * @event ModelEvent an event that is triggered before inserting a record.
+ * You may set [[ModelEvent::isValid]] to be false to stop the insertion.
+ */
+ const EVENT_BEFORE_INSERT = 'beforeInsert';
+ /**
+ * @event Event an event that is triggered after a record is inserted.
+ */
+ const EVENT_AFTER_INSERT = 'afterInsert';
+ /**
+ * @event ModelEvent an event that is triggered before updating a record.
+ * You may set [[ModelEvent::isValid]] to be false to stop the update.
+ */
+ const EVENT_BEFORE_UPDATE = 'beforeUpdate';
+ /**
+ * @event Event an event that is triggered after a record is updated.
+ */
+ const EVENT_AFTER_UPDATE = 'afterUpdate';
+ /**
+ * @event ModelEvent an event that is triggered before deleting a record.
+ * You may set [[ModelEvent::isValid]] to be false to stop the deletion.
+ */
+ const EVENT_BEFORE_DELETE = 'beforeDelete';
+ /**
+ * @event Event an event that is triggered after a record is deleted.
+ */
+ const EVENT_AFTER_DELETE = 'afterDelete';
+
+ /**
+ * @var array attribute values indexed by attribute names
+ */
+ private $_attributes = [];
+ /**
+ * @var array old attribute values indexed by attribute names.
+ */
+ private $_oldAttributes;
+ /**
+ * @var array related models indexed by the relation names
+ */
+ private $_related = [];
+
+
+ /**
+ * Creates an [[ActiveQuery]] instance for query purpose.
+ *
+ * @include @yii/db/ActiveRecord-find.md
+ *
+ * @param mixed $q the query parameter. This can be one of the followings:
+ *
+ * - a scalar value (integer or string): query by a single primary key value and return the
+ * corresponding record.
+ * - an array of name-value pairs: query by a set of column values and return a single record matching all of them.
+ * - null: return a new [[ActiveQuery]] object for further query purpose.
+ *
+ * @return ActiveQuery|ActiveRecord|null When `$q` is null, a new [[ActiveQuery]] instance
+ * is returned; when `$q` is a scalar or an array, an ActiveRecord object matching it will be
+ * returned (null will be returned if there is no matching).
+ * @throws InvalidConfigException if the AR class does not have a primary key
+ * @see createQuery()
+ */
+ public static function find($q = null)
+ {
+ $query = static::createQuery();
+ if (is_array($q)) {
+ return $query->andWhere($q)->one();
+ } elseif ($q !== null) {
+ // query by primary key
+ $primaryKey = static::primaryKey();
+ if (isset($primaryKey[0])) {
+ return $query->andWhere([$primaryKey[0] => $q])->one();
+ } else {
+ throw new InvalidConfigException(get_called_class() . ' must have a primary key.');
+ }
+ }
+ return $query;
+ }
+
+ /**
+ * Updates the whole table using the provided attribute values and conditions.
+ * For example, to change the status to be 1 for all customers whose status is 2:
+ *
+ * ~~~
+ * Customer::updateAll(['status' => 1], 'status = 2');
+ * ~~~
+ *
+ * @param array $attributes attribute values (name-value pairs) to be saved into the table
+ * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
+ * Please refer to [[Query::where()]] on how to specify this parameter.
+ * @param array $params the parameters (name => value) to be bound to the query.
+ * @return integer the number of rows updated
+ */
+ public static function updateAll($attributes, $condition = '')
+ {
+ throw new NotSupportedException(__METHOD__ . ' is not supported.');
+ }
+
+ /**
+ * Updates the whole table using the provided counter changes and conditions.
+ * For example, to increment all customers' age by 1,
+ *
+ * ~~~
+ * Customer::updateAllCounters(['age' => 1]);
+ * ~~~
+ *
+ * @param array $counters the counters to be updated (attribute name => increment value).
+ * Use negative values if you want to decrement the counters.
+ * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
+ * Please refer to [[Query::where()]] on how to specify this parameter.
+ * @return integer the number of rows updated
+ */
+ public static function updateAllCounters($counters, $condition = '')
+ {
+ throw new NotSupportedException(__METHOD__ . ' is not supported.');
+ }
+
+ /**
+ * Deletes rows in the table using the provided conditions.
+ * WARNING: If you do not specify any condition, this method will delete ALL rows in the table.
+ *
+ * For example, to delete all customers whose status is 3:
+ *
+ * ~~~
+ * Customer::deleteAll('status = 3');
+ * ~~~
+ *
+ * @param string|array $condition the conditions that will be put in the WHERE part of the DELETE SQL.
+ * Please refer to [[Query::where()]] on how to specify this parameter.
+ * @param array $params the parameters (name => value) to be bound to the query.
+ * @return integer the number of rows deleted
+ */
+ public static function deleteAll($condition = '', $params = [])
+ {
+ throw new NotSupportedException(__METHOD__ . ' is not supported.');
+ }
+
+ /**
+ * Returns the name of the column that stores the lock version for implementing optimistic locking.
+ *
+ * Optimistic locking allows multiple users to access the same record for edits and avoids
+ * potential conflicts. In case when a user attempts to save the record upon some staled data
+ * (because another user has modified the data), a [[StaleObjectException]] exception will be thrown,
+ * and the update or deletion is skipped.
+ *
+ * Optimistic locking is only supported by [[update()]] and [[delete()]].
+ *
+ * To use Optimistic locking:
+ *
+ * 1. Create a column to store the version number of each row. The column type should be `BIGINT DEFAULT 0`.
+ * Override this method to return the name of this column.
+ * 2. In the Web form that collects the user input, add a hidden field that stores
+ * the lock version of the recording being updated.
+ * 3. In the controller action that does the data updating, try to catch the [[StaleObjectException]]
+ * and implement necessary business logic (e.g. merging the changes, prompting stated data)
+ * to resolve the conflict.
+ *
+ * @return string the column name that stores the lock version of a table row.
+ * If null is returned (default implemented), optimistic locking will not be supported.
+ */
+ public function optimisticLock()
+ {
+ return null;
+ }
+
+ /**
+ * PHP getter magic method.
+ * This method is overridden so that attributes and related objects can be accessed like properties.
+ * @param string $name property name
+ * @return mixed property value
+ * @see getAttribute()
+ */
+ public function __get($name)
+ {
+ if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) {
+ return $this->_attributes[$name];
+ } elseif ($this->hasAttribute($name)) {
+ return null;
+ } else {
+ if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) {
+ return $this->_related[$name];
+ }
+ $value = parent::__get($name);
+ if ($value instanceof ActiveRelationInterface) {
+ if (method_exists($this, 'get' . $name)) {
+ $method = new \ReflectionMethod($this, 'get' . $name);
+ $realName = lcfirst(substr($method->getName(), 3));
+ if ($realName !== $name) {
+ throw new InvalidParamException('Relation names are case sensitive. ' . get_class($this) . " has a relation named \"$realName\" instead of \"$name\".");
+ }
+ }
+ return $this->_related[$name] = $value->multiple ? $value->all() : $value->one();
+ } else {
+ return $value;
+ }
+ }
+ }
+
+ /**
+ * PHP setter magic method.
+ * This method is overridden so that AR attributes can be accessed like properties.
+ * @param string $name property name
+ * @param mixed $value property value
+ */
+ public function __set($name, $value)
+ {
+ if ($this->hasAttribute($name)) {
+ $this->_attributes[$name] = $value;
+ } else {
+ parent::__set($name, $value);
+ }
+ }
+
+ /**
+ * Checks if a property value is null.
+ * This method overrides the parent implementation by checking if the named attribute is null or not.
+ * @param string $name the property name or the event name
+ * @return boolean whether the property value is null
+ */
+ public function __isset($name)
+ {
+ try {
+ return $this->__get($name) !== null;
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Sets a component property to be null.
+ * This method overrides the parent implementation by clearing
+ * the specified attribute value.
+ * @param string $name the property name or the event name
+ */
+ public function __unset($name)
+ {
+ if ($this->hasAttribute($name)) {
+ unset($this->_attributes[$name]);
+ } else {
+ if (isset($this->_related[$name])) {
+ unset($this->_related[$name]);
+ } else {
+ parent::__unset($name);
+ }
+ }
+ }
+
+ /**
+ * Declares a `has-one` relation.
+ * The declaration is returned in terms of an [[ActiveRelation]] instance
+ * through which the related record can be queried and retrieved back.
+ *
+ * A `has-one` relation means that there is at most one related record matching
+ * the criteria set by this relation, e.g., a customer has one country.
+ *
+ * For example, to declare the `country` relation for `Customer` class, we can write
+ * the following code in the `Customer` class:
+ *
+ * ~~~
+ * public function getCountry()
+ * {
+ * return $this->hasOne(Country::className(), ['id' => 'country_id']);
+ * }
+ * ~~~
+ *
+ * Note that in the above, the 'id' key in the `$link` parameter refers to an attribute name
+ * in the related class `Country`, while the 'country_id' value refers to an attribute name
+ * in the current AR class.
+ *
+ * Call methods declared in [[ActiveRelation]] to further customize the relation.
+ *
+ * @param string $class the class name of the related record
+ * @param array $link the primary-foreign key constraint. The keys of the array refer to
+ * the attributes of the record associated with the `$class` model, while the values of the
+ * array refer to the corresponding attributes in **this** AR class.
+ * @return ActiveRelationInterface the relation object.
+ */
+ public function hasOne($class, $link)
+ {
+ /** @var ActiveRecord $class */
+ return $class::createActiveRelation([
+ 'modelClass' => $class,
+ 'primaryModel' => $this,
+ 'link' => $link,
+ 'multiple' => false,
+ ]);
+ }
+
+ /**
+ * Declares a `has-many` relation.
+ * The declaration is returned in terms of an [[ActiveRelation]] instance
+ * through which the related record can be queried and retrieved back.
+ *
+ * A `has-many` relation means that there are multiple related records matching
+ * the criteria set by this relation, e.g., a customer has many orders.
+ *
+ * For example, to declare the `orders` relation for `Customer` class, we can write
+ * the following code in the `Customer` class:
+ *
+ * ~~~
+ * public function getOrders()
+ * {
+ * return $this->hasMany(Order::className(), ['customer_id' => 'id']);
+ * }
+ * ~~~
+ *
+ * Note that in the above, the 'customer_id' key in the `$link` parameter refers to
+ * an attribute name in the related class `Order`, while the 'id' value refers to
+ * an attribute name in the current AR class.
+ *
+ * @param string $class the class name of the related record
+ * @param array $link the primary-foreign key constraint. The keys of the array refer to
+ * the attributes of the record associated with the `$class` model, while the values of the
+ * array refer to the corresponding attributes in **this** AR class.
+ * @return ActiveRelationInterface the relation object.
+ */
+ public function hasMany($class, $link)
+ {
+ /** @var ActiveRecord $class */
+ return $class::createActiveRelation([
+ 'modelClass' => $class,
+ 'primaryModel' => $this,
+ 'link' => $link,
+ 'multiple' => true,
+ ]);
+ }
+
+ /**
+ * Populates the named relation with the related records.
+ * Note that this method does not check if the relation exists or not.
+ * @param string $name the relation name (case-sensitive)
+ * @param ActiveRecord|array|null the related records to be populated into the relation.
+ */
+ public function populateRelation($name, $records)
+ {
+ $this->_related[$name] = $records;
+ }
+
+ /**
+ * Check whether the named relation has been populated with records.
+ * @param string $name the relation name (case-sensitive)
+ * @return bool whether relation has been populated with records.
+ */
+ public function isRelationPopulated($name)
+ {
+ return array_key_exists($name, $this->_related);
+ }
+
+ /**
+ * Returns all populated related records.
+ * @return array an array of related records indexed by relation names.
+ */
+ public function getRelatedRecords()
+ {
+ return $this->_related;
+ }
+
+ /**
+ * Returns a value indicating whether the model has an attribute with the specified name.
+ * @param string $name the name of the attribute
+ * @return boolean whether the model has an attribute with the specified name.
+ */
+ public function hasAttribute($name)
+ {
+ return isset($this->_attributes[$name]) || in_array($name, $this->attributes());
+ }
+
+ /**
+ * Returns the named attribute value.
+ * If this record is the result of a query and the attribute is not loaded,
+ * null will be returned.
+ * @param string $name the attribute name
+ * @return mixed the attribute value. Null if the attribute is not set or does not exist.
+ * @see hasAttribute()
+ */
+ public function getAttribute($name)
+ {
+ return isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;
+ }
+
+ /**
+ * Sets the named attribute value.
+ * @param string $name the attribute name
+ * @param mixed $value the attribute value.
+ * @throws InvalidParamException if the named attribute does not exist.
+ * @see hasAttribute()
+ */
+ public function setAttribute($name, $value)
+ {
+ if ($this->hasAttribute($name)) {
+ $this->_attributes[$name] = $value;
+ } else {
+ throw new InvalidParamException(get_class($this) . ' has no attribute named "' . $name . '".');
+ }
+ }
+
+ /**
+ * Returns the old attribute values.
+ * @return array the old attribute values (name-value pairs)
+ */
+ public function getOldAttributes()
+ {
+ return $this->_oldAttributes === null ? [] : $this->_oldAttributes;
+ }
+
+ /**
+ * Sets the old attribute values.
+ * All existing old attribute values will be discarded.
+ * @param array $values old attribute values to be set.
+ */
+ public function setOldAttributes($values)
+ {
+ $this->_oldAttributes = $values;
+ }
+
+ /**
+ * Returns the old value of the named attribute.
+ * If this record is the result of a query and the attribute is not loaded,
+ * null will be returned.
+ * @param string $name the attribute name
+ * @return mixed the old attribute value. Null if the attribute is not loaded before
+ * or does not exist.
+ * @see hasAttribute()
+ */
+ public function getOldAttribute($name)
+ {
+ return isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
+ }
+
+ /**
+ * Sets the old value of the named attribute.
+ * @param string $name the attribute name
+ * @param mixed $value the old attribute value.
+ * @throws InvalidParamException if the named attribute does not exist.
+ * @see hasAttribute()
+ */
+ public function setOldAttribute($name, $value)
+ {
+ if (isset($this->_oldAttributes[$name]) || $this->hasAttribute($name)) {
+ $this->_oldAttributes[$name] = $value;
+ } else {
+ throw new InvalidParamException(get_class($this) . ' has no attribute named "' . $name . '".');
+ }
+ }
+
+ /**
+ * Returns a value indicating whether the named attribute has been changed.
+ * @param string $name the name of the attribute
+ * @return boolean whether the attribute has been changed
+ */
+ public function isAttributeChanged($name)
+ {
+ if (isset($this->_attributes[$name], $this->_oldAttributes[$name])) {
+ return $this->_attributes[$name] !== $this->_oldAttributes[$name];
+ } else {
+ return isset($this->_attributes[$name]) || isset($this->_oldAttributes[$name]);
+ }
+ }
+
+ /**
+ * Returns the attribute values that have been modified since they are loaded or saved most recently.
+ * @param string[]|null $names the names of the attributes whose values may be returned if they are
+ * changed recently. If null, [[attributes()]] will be used.
+ * @return array the changed attribute values (name-value pairs)
+ */
+ public function getDirtyAttributes($names = null)
+ {
+ if ($names === null) {
+ $names = $this->attributes();
+ }
+ $names = array_flip($names);
+ $attributes = [];
+ if ($this->_oldAttributes === null) {
+ foreach ($this->_attributes as $name => $value) {
+ if (isset($names[$name])) {
+ $attributes[$name] = $value;
+ }
+ }
+ } else {
+ foreach ($this->_attributes as $name => $value) {
+ if (isset($names[$name]) && (!array_key_exists($name, $this->_oldAttributes) || $value !== $this->_oldAttributes[$name])) {
+ $attributes[$name] = $value;
+ }
+ }
+ }
+ return $attributes;
+ }
+
+ /**
+ * Saves the current record.
+ *
+ * This method will call [[insert()]] when [[isNewRecord]] is true, or [[update()]]
+ * when [[isNewRecord]] is false.
+ *
+ * For example, to save a customer record:
+ *
+ * ~~~
+ * $customer = new Customer; // or $customer = Customer::find($id);
+ * $customer->name = $name;
+ * $customer->email = $email;
+ * $customer->save();
+ * ~~~
+ *
+ *
+ * @param boolean $runValidation whether to perform validation before saving the record.
+ * If the validation fails, the record will not be saved to database.
+ * @param array $attributes list of attributes that need to be saved. Defaults to null,
+ * meaning all attributes that are loaded from DB will be saved.
+ * @return boolean whether the saving succeeds
+ */
+ public function save($runValidation = true, $attributes = null)
+ {
+ if ($this->getIsNewRecord()) {
+ return $this->insert($runValidation, $attributes);
+ } else {
+ return $this->update($runValidation, $attributes) !== false;
+ }
+ }
+
+ /**
+ * Saves the changes to this active record into the associated database table.
+ *
+ * This method performs the following steps in order:
+ *
+ * 1. call [[beforeValidate()]] when `$runValidation` is true. If validation
+ * fails, it will skip the rest of the steps;
+ * 2. call [[afterValidate()]] when `$runValidation` is true.
+ * 3. call [[beforeSave()]]. If the method returns false, it will skip the
+ * rest of the steps;
+ * 4. save the record into database. If this fails, it will skip the rest of the steps;
+ * 5. call [[afterSave()]];
+ *
+ * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],
+ * [[EVENT_BEFORE_UPDATE]], [[EVENT_AFTER_UPDATE]] and [[EVENT_AFTER_VALIDATE]]
+ * will be raised by the corresponding methods.
+ *
+ * Only the [[changedAttributes|changed attribute values]] will be saved into database.
+ *
+ * For example, to update a customer record:
+ *
+ * ~~~
+ * $customer = Customer::find($id);
+ * $customer->name = $name;
+ * $customer->email = $email;
+ * $customer->update();
+ * ~~~
+ *
+ * Note that it is possible the update does not affect any row in the table.
+ * In this case, this method will return 0. For this reason, you should use the following
+ * code to check if update() is successful or not:
+ *
+ * ~~~
+ * if ($this->update() !== false) {
+ * // update successful
+ * } else {
+ * // update failed
+ * }
+ * ~~~
+ *
+ * @param boolean $runValidation whether to perform validation before saving the record.
+ * If the validation fails, the record will not be inserted into the database.
+ * @param array $attributes list of attributes that need to be saved. Defaults to null,
+ * meaning all attributes that are loaded from DB will be saved.
+ * @return integer|boolean the number of rows affected, or false if validation fails
+ * or [[beforeSave()]] stops the updating process.
+ * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
+ * being updated is outdated.
+ * @throws \Exception in case update failed.
+ */
+ public function update($runValidation = true, $attributes = null)
+ {
+ if ($runValidation && !$this->validate($attributes)) {
+ return false;
+ }
+ return $this->updateInternal($attributes);
+ }
+
+ /**
+ * @see CActiveRecord::update()
+ * @throws StaleObjectException
+ */
+ protected function updateInternal($attributes = null)
+ {
+ if (!$this->beforeSave(false)) {
+ return false;
+ }
+ $values = $this->getDirtyAttributes($attributes);
+ if (empty($values)) {
+ $this->afterSave(false);
+ return 0;
+ }
+ $condition = $this->getOldPrimaryKey(true);
+ $lock = $this->optimisticLock();
+ if ($lock !== null) {
+ if (!isset($values[$lock])) {
+ $values[$lock] = $this->$lock + 1;
+ }
+ $condition[$lock] = $this->$lock;
+ }
+ // We do not check the return value of updateAll() because it's possible
+ // that the UPDATE statement doesn't change anything and thus returns 0.
+ $rows = $this->updateAll($values, $condition);
+
+ if ($lock !== null && !$rows) {
+ throw new StaleObjectException('The object being updated is outdated.');
+ }
+
+ foreach ($values as $name => $value) {
+ $this->_oldAttributes[$name] = $this->_attributes[$name];
+ }
+ $this->afterSave(false);
+ return $rows;
+ }
+
+ /**
+ * Updates one or several counter columns for the current AR object.
+ * Note that this method differs from [[updateAllCounters()]] in that it only
+ * saves counters for the current AR object.
+ *
+ * An example usage is as follows:
+ *
+ * ~~~
+ * $post = Post::find($id);
+ * $post->updateCounters(['view_count' => 1]);
+ * ~~~
+ *
+ * @param array $counters the counters to be updated (attribute name => increment value)
+ * Use negative values if you want to decrement the counters.
+ * @return boolean whether the saving is successful
+ * @see updateAllCounters()
+ */
+ public function updateCounters($counters)
+ {
+ if ($this->updateAllCounters($counters, $this->getOldPrimaryKey(true)) > 0) {
+ foreach ($counters as $name => $value) {
+ $this->_attributes[$name] += $value;
+ $this->_oldAttributes[$name] = $this->_attributes[$name];
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Deletes the table row corresponding to this active record.
+ *
+ * This method performs the following steps in order:
+ *
+ * 1. call [[beforeDelete()]]. If the method returns false, it will skip the
+ * rest of the steps;
+ * 2. delete the record from the database;
+ * 3. call [[afterDelete()]].
+ *
+ * In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]]
+ * will be raised by the corresponding methods.
+ *
+ * @return integer|boolean the number of rows deleted, or false if the deletion is unsuccessful for some reason.
+ * Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.
+ * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
+ * being deleted is outdated.
+ * @throws \Exception in case delete failed.
+ */
+ public function delete()
+ {
+ $result = false;
+ if ($this->beforeDelete()) {
+ // we do not check the return value of deleteAll() because it's possible
+ // the record is already deleted in the database and thus the method will return 0
+ $condition = $this->getOldPrimaryKey(true);
+ $lock = $this->optimisticLock();
+ if ($lock !== null) {
+ $condition[$lock] = $this->$lock;
+ }
+ $result = $this->deleteAll($condition);
+ if ($lock !== null && !$result) {
+ throw new StaleObjectException('The object being deleted is outdated.');
+ }
+ $this->_oldAttributes = null;
+ $this->afterDelete();
+ }
+ return $result;
+ }
+
+ /**
+ * Returns a value indicating whether the current record is new.
+ * @return boolean whether the record is new and should be inserted when calling [[save()]].
+ */
+ public function getIsNewRecord()
+ {
+ return $this->_oldAttributes === null;
+ }
+
+ /**
+ * Sets the value indicating whether the record is new.
+ * @param boolean $value whether the record is new and should be inserted when calling [[save()]].
+ * @see getIsNewRecord()
+ */
+ public function setIsNewRecord($value)
+ {
+ $this->_oldAttributes = $value ? null : $this->_attributes;
+ }
+
+ /**
+ * Initializes the object.
+ * This method is called at the end of the constructor.
+ * The default implementation will trigger an [[EVENT_INIT]] event.
+ * If you override this method, make sure you call the parent implementation at the end
+ * to ensure triggering of the event.
+ */
+ public function init()
+ {
+ parent::init();
+ $this->trigger(self::EVENT_INIT);
+ }
+
+ /**
+ * This method is called when the AR object is created and populated with the query result.
+ * The default implementation will trigger an [[EVENT_AFTER_FIND]] event.
+ * When overriding this method, make sure you call the parent implementation to ensure the
+ * event is triggered.
+ */
+ public function afterFind()
+ {
+ $this->trigger(self::EVENT_AFTER_FIND);
+ }
+
+ /**
+ * This method is called at the beginning of inserting or updating a record.
+ * The default implementation will trigger an [[EVENT_BEFORE_INSERT]] event when `$insert` is true,
+ * or an [[EVENT_BEFORE_UPDATE]] event if `$insert` is false.
+ * When overriding this method, make sure you call the parent implementation like the following:
+ *
+ * ~~~
+ * public function beforeSave($insert)
+ * {
+ * if (parent::beforeSave($insert)) {
+ * // ...custom code here...
+ * return true;
+ * } else {
+ * return false;
+ * }
+ * }
+ * ~~~
+ *
+ * @param boolean $insert whether this method called while inserting a record.
+ * If false, it means the method is called while updating a record.
+ * @return boolean whether the insertion or updating should continue.
+ * If false, the insertion or updating will be cancelled.
+ */
+ public function beforeSave($insert)
+ {
+ $event = new ModelEvent;
+ $this->trigger($insert ? self::EVENT_BEFORE_INSERT : self::EVENT_BEFORE_UPDATE, $event);
+ return $event->isValid;
+ }
+
+ /**
+ * This method is called at the end of inserting or updating a record.
+ * The default implementation will trigger an [[EVENT_AFTER_INSERT]] event when `$insert` is true,
+ * or an [[EVENT_AFTER_UPDATE]] event if `$insert` is false.
+ * When overriding this method, make sure you call the parent implementation so that
+ * the event is triggered.
+ * @param boolean $insert whether this method called while inserting a record.
+ * If false, it means the method is called while updating a record.
+ */
+ public function afterSave($insert)
+ {
+ $this->trigger($insert ? self::EVENT_AFTER_INSERT : self::EVENT_AFTER_UPDATE);
+ }
+
+ /**
+ * This method is invoked before deleting a record.
+ * The default implementation raises the [[EVENT_BEFORE_DELETE]] event.
+ * When overriding this method, make sure you call the parent implementation like the following:
+ *
+ * ~~~
+ * public function beforeDelete()
+ * {
+ * if (parent::beforeDelete()) {
+ * // ...custom code here...
+ * return true;
+ * } else {
+ * return false;
+ * }
+ * }
+ * ~~~
+ *
+ * @return boolean whether the record should be deleted. Defaults to true.
+ */
+ public function beforeDelete()
+ {
+ $event = new ModelEvent;
+ $this->trigger(self::EVENT_BEFORE_DELETE, $event);
+ return $event->isValid;
+ }
+
+ /**
+ * This method is invoked after deleting a record.
+ * The default implementation raises the [[EVENT_AFTER_DELETE]] event.
+ * You may override this method to do postprocessing after the record is deleted.
+ * Make sure you call the parent implementation so that the event is raised properly.
+ */
+ public function afterDelete()
+ {
+ $this->trigger(self::EVENT_AFTER_DELETE);
+ }
+
+ /**
+ * Repopulates this active record with the latest data.
+ * @return boolean whether the row still exists in the database. If true, the latest data
+ * will be populated to this active record. Otherwise, this record will remain unchanged.
+ */
+ public function refresh()
+ {
+ $record = $this->find($this->getPrimaryKey(true));
+ if ($record === null) {
+ return false;
+ }
+ foreach ($this->attributes() as $name) {
+ $this->_attributes[$name] = isset($record->_attributes[$name]) ? $record->_attributes[$name] : null;
+ }
+ $this->_oldAttributes = $this->_attributes;
+ $this->_related = [];
+ return true;
+ }
+
+ /**
+ * Returns a value indicating whether the given active record is the same as the current one.
+ * The comparison is made by comparing the table names and the primary key values of the two active records.
+ * If one of the records [[isNewRecord|is new]] they are also considered not equal.
+ * @param ActiveRecord $record record to compare to
+ * @return boolean whether the two active records refer to the same row in the same database table.
+ */
+ public function equals($record)
+ {
+ if ($this->isNewRecord || $record->isNewRecord) {
+ return false;
+ }
+ return get_class($this) === get_class($record) && $this->getPrimaryKey() === $record->getPrimaryKey();
+ }
+
+ /**
+ * Returns the primary key value(s).
+ * @param boolean $asArray whether to return the primary key value as an array. If true,
+ * the return value will be an array with column names as keys and column values as values.
+ * Note that for composite primary keys, an array will always be returned regardless of this parameter value.
+ * @property mixed The primary key value. An array (column name => column value) is returned if
+ * the primary key is composite. A string is returned otherwise (null will be returned if
+ * the key value is null).
+ * @return mixed the primary key value. An array (column name => column value) is returned if the primary key
+ * is composite or `$asArray` is true. A string is returned otherwise (null will be returned if
+ * the key value is null).
+ */
+ public function getPrimaryKey($asArray = false)
+ {
+ $keys = $this->primaryKey();
+ if (count($keys) === 1 && !$asArray) {
+ return isset($this->_attributes[$keys[0]]) ? $this->_attributes[$keys[0]] : null;
+ } else {
+ $values = [];
+ foreach ($keys as $name) {
+ $values[$name] = isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;
+ }
+ return $values;
+ }
+ }
+
+ /**
+ * Returns the old primary key value(s).
+ * This refers to the primary key value that is populated into the record
+ * after executing a find method (e.g. find(), findAll()).
+ * The value remains unchanged even if the primary key attribute is manually assigned with a different value.
+ * @param boolean $asArray whether to return the primary key value as an array. If true,
+ * the return value will be an array with column name as key and column value as value.
+ * If this is false (default), a scalar value will be returned for non-composite primary key.
+ * @property mixed The old primary key value. An array (column name => column value) is
+ * returned if the primary key is composite. A string is returned otherwise (null will be
+ * returned if the key value is null).
+ * @return mixed the old primary key value. An array (column name => column value) is returned if the primary key
+ * is composite or `$asArray` is true. A string is returned otherwise (null will be returned if
+ * the key value is null).
+ */
+ public function getOldPrimaryKey($asArray = false)
+ {
+ $keys = $this->primaryKey();
+ if (count($keys) === 1 && !$asArray) {
+ return isset($this->_oldAttributes[$keys[0]]) ? $this->_oldAttributes[$keys[0]] : null;
+ } else {
+ $values = [];
+ foreach ($keys as $name) {
+ $values[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
+ }
+ return $values;
+ }
+ }
+
+ /**
+ * Creates an active record object using a row of data.
+ * This method is called by [[ActiveQuery]] to populate the query results
+ * into Active Records. It is not meant to be used to create new records.
+ * @param array $row attribute values (name => value)
+ * @return ActiveRecord the newly created active record.
+ */
+ public static function create($row)
+ {
+ $record = static::instantiate($row);
+ $columns = array_flip($record->attributes());
+ foreach ($row as $name => $value) {
+ if (isset($columns[$name])) {
+ $record->_attributes[$name] = $value;
+ } else {
+ $record->$name = $value;
+ }
+ }
+ $record->_oldAttributes = $record->_attributes;
+ $record->afterFind();
+ return $record;
+ }
+
+ /**
+ * Creates an active record instance.
+ * This method is called by [[create()]].
+ * You may override this method if the instance being created
+ * depends on the row data to be populated into the record.
+ * For example, by creating a record based on the value of a column,
+ * you may implement the so-called single-table inheritance mapping.
+ * @param array $row row data to be populated into the record.
+ * @return ActiveRecord the newly created active record
+ */
+ public static function instantiate($row)
+ {
+ return new static;
+ }
+
+ /**
+ * Returns whether there is an element at the specified offset.
+ * This method is required by the interface ArrayAccess.
+ * @param mixed $offset the offset to check on
+ * @return boolean whether there is an element at the specified offset.
+ */
+ public function offsetExists($offset)
+ {
+ return $this->__isset($offset);
+ }
+
+ /**
+ * Returns the relation object with the specified name.
+ * A relation is defined by a getter method which returns an [[ActiveRelation]] object.
+ * It can be declared in either the Active Record class itself or one of its behaviors.
+ * @param string $name the relation name
+ * @return ActiveRelation the relation object
+ * @throws InvalidParamException if the named relation does not exist.
+ */
+ public function getRelation($name)
+ {
+ $getter = 'get' . $name;
+ try {
+ // the relation could be defined in a behavior
+ $relation = $this->$getter();
+ } catch (UnknownMethodException $e) {
+ throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e);
+ }
+ if (!$relation instanceof ActiveRelationInterface) {
+ throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".');
+ }
+
+ if (method_exists($this, $getter)) {
+ // relation name is case sensitive, trying to validate it when the relation is defined within this class
+ $method = new \ReflectionMethod($this, $getter);
+ $realName = lcfirst(substr($method->getName(), 3));
+ if ($realName !== $name) {
+ throw new InvalidParamException('Relation names are case sensitive. ' . get_class($this) . " has a relation named \"$realName\" instead of \"$name\".");
+ }
+ }
+
+ return $relation;
+ }
+
+ /**
+ * Establishes the relationship between two models.
+ *
+ * The relationship is established by setting the foreign key value(s) in one model
+ * to be the corresponding primary key value(s) in the other model.
+ * The model with the foreign key will be saved into database without performing validation.
+ *
+ * If the relationship involves a pivot table, a new row will be inserted into the
+ * pivot table which contains the primary key values from both models.
+ *
+ * Note that this method requires that the primary key value is not null.
+ *
+ * @param string $name the case sensitive name of the relationship
+ * @param ActiveRecord $model the model to be linked with the current one.
+ * @param array $extraColumns additional column values to be saved into the pivot table.
+ * This parameter is only meaningful for a relationship involving a pivot table
+ * (i.e., a relation set with `[[ActiveRelation::via()]]` or `[[ActiveRelation::viaTable()]]`.)
+ * @throws InvalidCallException if the method is unable to link two models.
+ */
+ public function link($name, $model, $extraColumns = [])
+ {
+ $relation = $this->getRelation($name);
+
+ if ($relation->via !== null) {
+ if ($this->getIsNewRecord() || $model->getIsNewRecord()) {
+ throw new InvalidCallException('Unable to link models: both models must NOT be newly created.');
+ }
+ if (is_array($relation->via)) {
+ /** @var ActiveRelation $viaRelation */
+ list($viaName, $viaRelation) = $relation->via;
+ $viaClass = $viaRelation->modelClass;
+ // unset $viaName so that it can be reloaded to reflect the change
+ unset($this->_related[$viaName]);
+ } else {
+ $viaRelation = $relation->via;
+ $viaTable = reset($relation->via->from);
+ }
+ $columns = [];
+ foreach ($viaRelation->link as $a => $b) {
+ $columns[$a] = $this->$b;
+ }
+ foreach ($relation->link as $a => $b) {
+ $columns[$b] = $model->$a;
+ }
+ foreach ($extraColumns as $k => $v) {
+ $columns[$k] = $v;
+ }
+ if (is_array($relation->via)) {
+ /** @var $viaClass ActiveRecord */
+ /** @var $record ActiveRecord */
+ $record = new $viaClass();
+ foreach($columns as $column => $value) {
+ $record->$column = $value;
+ }
+ $record->insert(false);
+ } else {
+ /** @var $viaTable string */
+ static::getDb()->createCommand()
+ ->insert($viaTable, $columns)->execute();
+ }
+ } else {
+ $p1 = $model->isPrimaryKey(array_keys($relation->link));
+ $p2 = $this->isPrimaryKey(array_values($relation->link));
+ if ($p1 && $p2) {
+ if ($this->getIsNewRecord() && $model->getIsNewRecord()) {
+ throw new InvalidCallException('Unable to link models: both models are newly created.');
+ } elseif ($this->getIsNewRecord()) {
+ $this->bindModels(array_flip($relation->link), $this, $model);
+ } else {
+ $this->bindModels($relation->link, $model, $this);
+ }
+ } elseif ($p1) {
+ $this->bindModels(array_flip($relation->link), $this, $model);
+ } elseif ($p2) {
+ $this->bindModels($relation->link, $model, $this);
+ } else {
+ throw new InvalidCallException('Unable to link models: the link does not involve any primary key.');
+ }
+ }
+
+ // update lazily loaded related objects
+ if (!$relation->multiple) {
+ $this->_related[$name] = $model;
+ } elseif (isset($this->_related[$name])) {
+ if ($relation->indexBy !== null) {
+ $indexBy = $relation->indexBy;
+ $this->_related[$name][$model->$indexBy] = $model;
+ } else {
+ $this->_related[$name][] = $model;
+ }
+ }
+ }
+
+ /**
+ * Destroys the relationship between two models.
+ *
+ * The model with the foreign key of the relationship will be deleted if `$delete` is true.
+ * Otherwise, the foreign key will be set null and the model will be saved without validation.
+ *
+ * @param string $name the case sensitive name of the relationship.
+ * @param ActiveRecord $model the model to be unlinked from the current one.
+ * @param boolean $delete whether to delete the model that contains the foreign key.
+ * If false, the model's foreign key will be set null and saved.
+ * If true, the model containing the foreign key will be deleted.
+ * @throws InvalidCallException if the models cannot be unlinked
+ */
+ public function unlink($name, $model, $delete = false)
+ {
+ $relation = $this->getRelation($name);
+
+ if ($relation->via !== null) {
+ if (is_array($relation->via)) {
+ /** @var ActiveRelation $viaRelation */
+ list($viaName, $viaRelation) = $relation->via;
+ $viaClass = $viaRelation->modelClass;
+ unset($this->_related[$viaName]);
+ } else {
+ $viaRelation = $relation->via;
+ $viaTable = reset($relation->via->from);
+ }
+ $columns = [];
+ foreach ($viaRelation->link as $a => $b) {
+ $columns[$a] = $this->$b;
+ }
+ foreach ($relation->link as $a => $b) {
+ $columns[$b] = $model->$a;
+ }
+ if (is_array($relation->via)) {
+ /** @var $viaClass ActiveRecord */
+ if ($delete) {
+ $viaClass::deleteAll($columns);
+ } else {
+ $nulls = [];
+ foreach (array_keys($columns) as $a) {
+ $nulls[$a] = null;
+ }
+ $viaClass::updateAll($nulls, $columns);
+ }
+ } else {
+ /** @var $viaTable string */
+ $command = static::getDb()->createCommand();
+ if ($delete) {
+ $command->delete($viaTable, $columns)->execute();
+ } else {
+ $nulls = [];
+ foreach (array_keys($columns) as $a) {
+ $nulls[$a] = null;
+ }
+ $command->update($viaTable, $nulls, $columns)->execute();
+ }
+ }
+ } else {
+ $p1 = $model->isPrimaryKey(array_keys($relation->link));
+ $p2 = $this->isPrimaryKey(array_values($relation->link));
+ if ($p1 && $p2 || $p2) {
+ foreach ($relation->link as $a => $b) {
+ $model->$a = null;
+ }
+ $delete ? $model->delete() : $model->save(false);
+ } elseif ($p1) {
+ foreach ($relation->link as $b) {
+ $this->$b = null;
+ }
+ $delete ? $this->delete() : $this->save(false);
+ } else {
+ throw new InvalidCallException('Unable to unlink models: the link does not involve any primary key.');
+ }
+ }
+
+ if (!$relation->multiple) {
+ unset($this->_related[$name]);
+ } elseif (isset($this->_related[$name])) {
+ /** @var ActiveRecord $b */
+ foreach ($this->_related[$name] as $a => $b) {
+ if ($model->getPrimaryKey() == $b->getPrimaryKey()) {
+ unset($this->_related[$name][$a]);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param array $link
+ * @param ActiveRecord $foreignModel
+ * @param ActiveRecord $primaryModel
+ * @throws InvalidCallException
+ */
+ private function bindModels($link, $foreignModel, $primaryModel)
+ {
+ foreach ($link as $fk => $pk) {
+ $value = $primaryModel->$pk;
+ if ($value === null) {
+ throw new InvalidCallException('Unable to link models: the primary key of ' . get_class($primaryModel) . ' is null.');
+ }
+ $foreignModel->$fk = $value;
+ }
+ $foreignModel->save(false);
+ }
+
+ /**
+ * Returns a value indicating whether the given set of attributes represents the primary key for this model
+ * @param array $keys the set of attributes to check
+ * @return boolean whether the given set of attributes represents the primary key for this model
+ */
+ public static function isPrimaryKey($keys)
+ {
+ $pks = static::primaryKey();
+ if (count($keys) === count($pks)) {
+ return count(array_intersect($keys, $pks)) === count($pks);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/framework/yii/db/ColumnSchema.php b/framework/yii/db/ColumnSchema.php
index ffdafd4..3e7f6cf 100644
--- a/framework/yii/db/ColumnSchema.php
+++ b/framework/yii/db/ColumnSchema.php
@@ -7,13 +7,15 @@
namespace yii\db;
+use yii\base\Object;
+
/**
* ColumnSchema class describes the metadata of a column in a database table.
*
* @author Qiang Xue
* @since 2.0
*/
-class ColumnSchema extends \yii\base\Component
+class ColumnSchema extends Object
{
/**
* @var string name of this column (without quotes).
@@ -85,6 +87,9 @@ class ColumnSchema extends \yii\base\Component
*/
public function typecast($value)
{
+ if ($value === '' && $this->type !== Schema::TYPE_TEXT && $this->type !== Schema::TYPE_STRING && $this->type !== Schema::TYPE_BINARY) {
+ return null;
+ }
if ($value === null || gettype($value) === $this->phpType || $value instanceof Expression) {
return $value;
}
diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php
index 17accf4..2075b2d 100644
--- a/framework/yii/db/Command.php
+++ b/framework/yii/db/Command.php
@@ -19,7 +19,7 @@ use yii\caching\Cache;
*
* To execute a non-query SQL (such as INSERT, DELETE, UPDATE), call [[execute()]].
* To execute a SQL statement that returns result data set (such as SELECT),
- * use [[queryAll()]], [[queryRow()]], [[queryColumn()]], [[queryScalar()]], or [[query()]].
+ * use [[queryAll()]], [[queryOne()]], [[queryColumn()]], [[queryScalar()]], or [[query()]].
* For example,
*
* ~~~
@@ -36,15 +36,17 @@ use yii\caching\Cache;
* [[update()]], etc. For example,
*
* ~~~
- * $connection->createCommand()->insert('tbl_user', array(
+ * $connection->createCommand()->insert('tbl_user', [
* 'name' => 'Sam',
* 'age' => 30,
- * ))->execute();
+ * ])->execute();
* ~~~
*
* To build SELECT SQL statements, please use [[QueryBuilder]] instead.
*
- * @property string $sql the SQL statement to be executed
+ * @property string $rawSql The raw SQL with parameter values inserted into the corresponding placeholders in
+ * [[sql]]. This property is read-only.
+ * @property string $sql The SQL statement to be executed.
*
* @author Qiang Xue
* @since 2.0
@@ -60,18 +62,20 @@ class Command extends \yii\base\Component
*/
public $pdoStatement;
/**
- * @var mixed the default fetch mode for this command.
+ * @var integer the default fetch mode for this command.
* @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php
*/
public $fetchMode = \PDO::FETCH_ASSOC;
/**
- * @var string the SQL statement that this command represents
+ * @var array the parameters (name => value) that are bound to the current PDO statement.
+ * This property is maintained by methods such as [[bindValue()]].
+ * Do not modify it directly.
*/
- private $_sql;
+ public $params = [];
/**
- * @var array the parameter log information (name => value)
+ * @var string the SQL statement that this command represents
*/
- private $_params = array();
+ private $_sql;
/**
* Returns the SQL statement for this command.
@@ -86,14 +90,14 @@ class Command extends \yii\base\Component
* Specifies the SQL statement to be executed.
* The previous SQL execution (if any) will be cancelled, and [[params]] will be cleared as well.
* @param string $sql the SQL statement to be set.
- * @return Command this command instance
+ * @return static this command instance
*/
public function setSql($sql)
{
if ($sql !== $this->_sql) {
$this->cancel();
$this->_sql = $this->db->quoteSql($sql);
- $this->_params = array();
+ $this->params = [];
}
return $this;
}
@@ -102,15 +106,15 @@ class Command extends \yii\base\Component
* Returns the raw SQL by inserting parameter values into the corresponding placeholders in [[sql]].
* Note that the return value of this method should mainly be used for logging purpose.
* It is likely that this method returns an invalid SQL due to improper replacement of parameter placeholders.
- * @return string the raw SQL
+ * @return string the raw SQL with parameter values inserted into the corresponding placeholders in [[sql]].
*/
public function getRawSql()
{
- if (empty($this->_params)) {
+ if (empty($this->params)) {
return $this->_sql;
} else {
- $params = array();
- foreach ($this->_params as $name => $value) {
+ $params = [];
+ foreach ($this->params as $name => $value) {
if (is_string($value)) {
$params[$name] = $this->db->quoteValue($value);
} elseif ($value === null) {
@@ -146,9 +150,9 @@ class Command extends \yii\base\Component
try {
$this->pdoStatement = $this->db->pdo->prepare($sql);
} catch (\Exception $e) {
- Yii::error($e->getMessage() . "\nFailed to prepare SQL: $sql", __METHOD__);
+ $message = $e->getMessage() . "\nFailed to prepare SQL: $sql";
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
- throw new Exception($e->getMessage(), $errorInfo, (int)$e->getCode(), $e);
+ throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
}
}
}
@@ -172,22 +176,23 @@ class Command extends \yii\base\Component
* @param integer $dataType SQL data type of the parameter. If null, the type is determined by the PHP type of the value.
* @param integer $length length of the data type
* @param mixed $driverOptions the driver-specific options
- * @return Command the current command being executed
+ * @return static the current command being executed
* @see http://www.php.net/manual/en/function.PDOStatement-bindParam.php
*/
public function bindParam($name, &$value, $dataType = null, $length = null, $driverOptions = null)
{
$this->prepare();
if ($dataType === null) {
- $this->pdoStatement->bindParam($name, $value, $this->getPdoType($value));
- } elseif ($length === null) {
+ $dataType = $this->db->getSchema()->getPdoType($value);
+ }
+ if ($length === null) {
$this->pdoStatement->bindParam($name, $value, $dataType);
} elseif ($driverOptions === null) {
$this->pdoStatement->bindParam($name, $value, $dataType, $length);
} else {
$this->pdoStatement->bindParam($name, $value, $dataType, $length, $driverOptions);
}
- $this->_params[$name] =& $value;
+ $this->params[$name] =& $value;
return $this;
}
@@ -199,18 +204,17 @@ class Command extends \yii\base\Component
* placeholders, this will be the 1-indexed position of the parameter.
* @param mixed $value The value to bind to the parameter
* @param integer $dataType SQL data type of the parameter. If null, the type is determined by the PHP type of the value.
- * @return Command the current command being executed
+ * @return static the current command being executed
* @see http://www.php.net/manual/en/function.PDOStatement-bindValue.php
*/
public function bindValue($name, $value, $dataType = null)
{
$this->prepare();
if ($dataType === null) {
- $this->pdoStatement->bindValue($name, $value, $this->getPdoType($value));
- } else {
- $this->pdoStatement->bindValue($name, $value, $dataType);
+ $dataType = $this->db->getSchema()->getPdoType($value);
}
- $this->_params[$name] = $value;
+ $this->pdoStatement->bindValue($name, $value, $dataType);
+ $this->params[$name] = $value;
return $this;
}
@@ -220,10 +224,10 @@ class Command extends \yii\base\Component
* Note that the SQL data type of each value is determined by its PHP type.
* @param array $values the values to be bound. This must be given in terms of an associative
* array with array keys being the parameter names, and array values the corresponding parameter values,
- * e.g. `array(':name' => 'John', ':age' => 25)`. By default, the PDO type of each value is determined
- * by its PHP type. You may explicitly specify the PDO type by using an array: `array(value, type)`,
- * e.g. `array(':name' => 'John', ':profile' => array($profile, \PDO::PARAM_LOB))`.
- * @return Command the current command being executed
+ * e.g. `[':name' => 'John', ':age' => 25]`. By default, the PDO type of each value is determined
+ * by its PHP type. You may explicitly specify the PDO type by using an array: `[value, type]`,
+ * e.g. `[':name' => 'John', ':profile' => [$profile, \PDO::PARAM_LOB]]`.
+ * @return static the current command being executed
*/
public function bindValues($values)
{
@@ -234,35 +238,16 @@ class Command extends \yii\base\Component
$type = $value[1];
$value = $value[0];
} else {
- $type = $this->getPdoType($value);
+ $type = $this->db->getSchema()->getPdoType($value);
}
$this->pdoStatement->bindValue($name, $value, $type);
- $this->_params[$name] = $value;
+ $this->params[$name] = $value;
}
}
return $this;
}
/**
- * Determines the PDO type for the give PHP data value.
- * @param mixed $data the data whose PDO type is to be determined
- * @return integer the PDO type
- * @see http://www.php.net/manual/en/pdo.constants.php
- */
- private function getPdoType($data)
- {
- static $typeMap = array(
- 'boolean' => \PDO::PARAM_BOOL,
- 'integer' => \PDO::PARAM_INT,
- 'string' => \PDO::PARAM_STR,
- 'resource' => \PDO::PARAM_LOB,
- 'NULL' => \PDO::PARAM_NULL,
- );
- $type = gettype($data);
- return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
- }
-
- /**
* Executes the SQL statement.
* This method should only be used for executing non-query SQL statement, such as `INSERT`, `DELETE`, `UPDATE` SQLs.
* No result set will be returned.
@@ -275,14 +260,14 @@ class Command extends \yii\base\Component
$rawSql = $this->getRawSql();
- Yii::trace("Executing SQL: $rawSql", __METHOD__);
+ Yii::info($rawSql, __METHOD__);
if ($sql == '') {
return 0;
}
+ $token = $rawSql;
try {
- $token = "SQL: $sql";
Yii::beginProfile($token, __METHOD__);
$this->prepare();
@@ -293,12 +278,13 @@ class Command extends \yii\base\Component
return $n;
} catch (\Exception $e) {
Yii::endProfile($token, __METHOD__);
- $message = $e->getMessage();
-
- Yii::error("$message\nFailed to execute SQL: $rawSql", __METHOD__);
-
- $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
- throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
+ if ($e instanceof Exception) {
+ throw $e;
+ } else {
+ $message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
+ $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
+ throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
+ }
}
}
@@ -315,7 +301,7 @@ class Command extends \yii\base\Component
/**
* Executes the SQL statement and returns ALL rows at once.
- * @param mixed $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)
+ * @param integer $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)
* for valid fetch modes. If this parameter is null, the value set in [[fetchMode]] will be used.
* @return array all rows of the query result. Each array element is an array representing a row of data.
* An empty array is returned if the query results in nothing.
@@ -329,13 +315,13 @@ class Command extends \yii\base\Component
/**
* Executes the SQL statement and returns the first row of the result.
* This method is best used when only the first row of result is needed for a query.
- * @param mixed $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)
+ * @param integer $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)
* for valid fetch modes. If this parameter is null, the value set in [[fetchMode]] will be used.
* @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query
* results in nothing.
* @throws Exception execution failed
*/
- public function queryRow($fetchMode = null)
+ public function queryOne($fetchMode = null)
{
return $this->queryInternal('fetch', $fetchMode);
}
@@ -372,7 +358,7 @@ class Command extends \yii\base\Component
/**
* Performs the actual DB query of a SQL statement.
* @param string $method method of PDOStatement to be called
- * @param mixed $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)
+ * @param integer $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)
* for valid fetch modes. If this parameter is null, the value set in [[fetchMode]] will be used.
* @return mixed the method execution result
* @throws Exception if the query causes any problem
@@ -380,31 +366,30 @@ class Command extends \yii\base\Component
private function queryInternal($method, $fetchMode = null)
{
$db = $this->db;
- $sql = $this->getSql();
$rawSql = $this->getRawSql();
- Yii::trace("Querying SQL: $rawSql", __METHOD__);
+ Yii::info($rawSql, __METHOD__);
- /** @var $cache \yii\caching\Cache */
+ /** @var \yii\caching\Cache $cache */
if ($db->enableQueryCache && $method !== '') {
$cache = is_string($db->queryCache) ? Yii::$app->getComponent($db->queryCache) : $db->queryCache;
}
if (isset($cache) && $cache instanceof Cache) {
- $cacheKey = array(
+ $cacheKey = [
__CLASS__,
$db->dsn,
$db->username,
$rawSql,
- );
+ ];
if (($result = $cache->get($cacheKey)) !== false) {
Yii::trace('Query result served from cache', __METHOD__);
return $result;
}
}
+ $token = $rawSql;
try {
- $token = "SQL: $sql";
Yii::beginProfile($token, __METHOD__);
$this->prepare();
@@ -416,7 +401,7 @@ class Command extends \yii\base\Component
if ($fetchMode === null) {
$fetchMode = $this->fetchMode;
}
- $result = call_user_func_array(array($this->pdoStatement, $method), (array)$fetchMode);
+ $result = call_user_func_array([$this->pdoStatement, $method], (array)$fetchMode);
$this->pdoStatement->closeCursor();
}
@@ -430,10 +415,13 @@ class Command extends \yii\base\Component
return $result;
} catch (\Exception $e) {
Yii::endProfile($token, __METHOD__);
- $message = $e->getMessage();
- Yii::error("$message\nCommand::$method() failed: $rawSql", __METHOD__);
- $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
- throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
+ if ($e instanceof Exception) {
+ throw $e;
+ } else {
+ $message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
+ $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
+ throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
+ }
}
}
@@ -442,10 +430,10 @@ class Command extends \yii\base\Component
* For example,
*
* ~~~
- * $connection->createCommand()->insert('tbl_user', array(
+ * $connection->createCommand()->insert('tbl_user', [
* 'name' => 'Sam',
* 'age' => 30,
- * ))->execute();
+ * ])->execute();
* ~~~
*
* The method will properly escape the column names, and bind the values to be inserted.
@@ -458,7 +446,7 @@ class Command extends \yii\base\Component
*/
public function insert($table, $columns)
{
- $params = array();
+ $params = [];
$sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
return $this->setSql($sql)->bindValues($params);
}
@@ -468,14 +456,14 @@ class Command extends \yii\base\Component
* For example,
*
* ~~~
- * $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
- * array('Tom', 30),
- * array('Jane', 20),
- * array('Linda', 25),
- * ))->execute();
+ * $connection->createCommand()->batchInsert('tbl_user', ['name', 'age'], [
+ * ['Tom', 30],
+ * ['Jane', 20],
+ * ['Linda', 25],
+ * ])->execute();
* ~~~
*
- * Not that the values in each row must match the corresponding column names.
+ * Note that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
@@ -493,9 +481,7 @@ class Command extends \yii\base\Component
* For example,
*
* ~~~
- * $connection->createCommand()->update('tbl_user', array(
- * 'status' => 1,
- * ), 'age > 30')->execute();
+ * $connection->createCommand()->update('tbl_user', ['status' => 1], 'age > 30')->execute();
* ~~~
*
* The method will properly escape the column names and bind the values to be updated.
@@ -504,12 +490,12 @@ class Command extends \yii\base\Component
*
* @param string $table the table to be updated.
* @param array $columns the column data (name => value) to be updated.
- * @param mixed $condition the condition that will be put in the WHERE part. Please
+ * @param string|array $condition the condition that will be put in the WHERE part. Please
* refer to [[Query::where()]] on how to specify condition.
* @param array $params the parameters to be bound to the command
* @return Command the command object itself
*/
- public function update($table, $columns, $condition = '', $params = array())
+ public function update($table, $columns, $condition = '', $params = [])
{
$sql = $this->db->getQueryBuilder()->update($table, $columns, $condition, $params);
return $this->setSql($sql)->bindValues($params);
@@ -528,12 +514,12 @@ class Command extends \yii\base\Component
* Note that the created command is not executed until [[execute()]] is called.
*
* @param string $table the table where the data will be deleted from.
- * @param mixed $condition the condition that will be put in the WHERE part. Please
+ * @param string|array $condition the condition that will be put in the WHERE part. Please
* refer to [[Query::where()]] on how to specify condition.
* @param array $params the parameters to be bound to the command
* @return Command the command object itself
*/
- public function delete($table, $condition = '', $params = array())
+ public function delete($table, $condition = '', $params = [])
{
$sql = $this->db->getQueryBuilder()->delete($table, $condition, $params);
return $this->setSql($sql)->bindValues($params);
@@ -654,6 +640,32 @@ class Command extends \yii\base\Component
}
/**
+ * Creates a SQL command for adding a primary key constraint to an existing table.
+ * The method will properly quote the table and column names.
+ * @param string $name the name of the primary key constraint.
+ * @param string $table the table that the primary key constraint will be added to.
+ * @param string|array $columns comma separated string or array of columns that the primary key will consist of.
+ * @return Command the command object itself.
+ */
+ public function addPrimaryKey($name, $table, $columns)
+ {
+ $sql = $this->db->getQueryBuilder()->addPrimaryKey($name, $table, $columns);
+ return $this->setSql($sql);
+ }
+
+ /**
+ * Creates a SQL command for removing a primary key constraint to an existing table.
+ * @param string $name the name of the primary key constraint to be removed.
+ * @param string $table the table that the primary key constraint will be removed from.
+ * @return Command the command object itself
+ */
+ public function dropPrimaryKey($name, $table)
+ {
+ $sql = $this->db->getQueryBuilder()->dropPrimaryKey($name, $table);
+ return $this->setSql($sql);
+ }
+
+ /**
* Creates a SQL command for adding a foreign key constraint to an existing table.
* The method will properly quote the table and column names.
* @param string $name the name of the foreign key constraint.
diff --git a/framework/yii/db/Connection.php b/framework/yii/db/Connection.php
index 0dd47d8..ef5be87 100644
--- a/framework/yii/db/Connection.php
+++ b/framework/yii/db/Connection.php
@@ -28,11 +28,11 @@ use yii\caching\Cache;
* the DB connection:
*
* ~~~
- * $connection = new \yii\db\Connection(array(
+ * $connection = new \yii\db\Connection([
* 'dsn' => $dsn,
* 'username' => $username,
* 'password' => $password,
- * ));
+ * ]);
* $connection->open();
* ~~~
*
@@ -76,26 +76,29 @@ use yii\caching\Cache;
* configuration like the following:
*
* ~~~
- * array(
- * 'components' => array(
- * 'db' => array(
+ * [
+ * 'components' => [
+ * 'db' => [
* 'class' => '\yii\db\Connection',
* 'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
* 'username' => 'root',
* 'password' => '',
* 'charset' => 'utf8',
- * ),
- * ),
- * )
+ * ],
+ * ],
+ * ]
* ~~~
*
+ * @property string $driverName Name of the DB driver. This property is read-only.
* @property boolean $isActive Whether the DB connection is established. This property is read-only.
- * @property Transaction $transaction The currently active transaction. Null if no active transaction.
- * @property Schema $schema The database schema information for the current connection.
- * @property QueryBuilder $queryBuilder The query builder.
- * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the sequence object.
- * @property string $driverName Name of the DB driver currently being used.
- * @property array $querySummary The statistical results of SQL queries.
+ * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the
+ * sequence object. This property is read-only.
+ * @property QueryBuilder $queryBuilder The query builder for the current DB connection. This property is
+ * read-only.
+ * @property Schema $schema The schema information for the database opened by this connection. This property
+ * is read-only.
+ * @property Transaction $transaction The currently active transaction. Null if no active transaction. This
+ * property is read-only.
*
* @author Qiang Xue
* @since 2.0
@@ -156,7 +159,7 @@ class Connection extends Component
* The table names may contain schema prefix, if any. Do not quote the table names.
* @see enableSchemaCache
*/
- public $schemaCacheExclude = array();
+ public $schemaCacheExclude = [];
/**
* @var Cache|string the cache object or the ID of the cache application component that
* is used to cache the table metadata.
@@ -198,12 +201,11 @@ class Connection extends Component
public $queryCache = 'cache';
/**
* @var string the charset used for database connection. The property is only used
- * for MySQL and PostgreSQL databases. Defaults to null, meaning using default charset
+ * for MySQL, PostgreSQL and CUBRID databases. Defaults to null, meaning using default charset
* as specified by the database.
*
* Note that if you're using GBK or BIG5 then it's highly recommended to
- * update to PHP 5.3.6+ and to specify charset via DSN like
- * 'mysql:dbname=mydatabase;host=127.0.0.1;charset=GBK;'.
+ * specify charset via DSN like 'mysql:dbname=mydatabase;host=127.0.0.1;charset=GBK;'.
*/
public $charset;
/**
@@ -231,7 +233,7 @@ class Connection extends Component
* You normally do not need to set this property unless you want to use your own
* [[Schema]] class to support DBMS that is not supported by Yii.
*/
- public $schemaMap = array(
+ public $schemaMap = [
'pgsql' => 'yii\db\pgsql\Schema', // PostgreSQL
'mysqli' => 'yii\db\mysql\Schema', // MySQL
'mysql' => 'yii\db\mysql\Schema', // MySQL
@@ -241,7 +243,8 @@ class Connection extends Component
'oci' => 'yii\db\oci\Schema', // Oracle driver
'mssql' => 'yii\db\mssql\Schema', // older MSSQL driver on MS Windows hosts
'dblib' => 'yii\db\mssql\Schema', // dblib drivers on GNU/Linux (and maybe other OSes) hosts
- );
+ 'cubrid' => 'yii\db\cubrid\Schema', // CUBRID
+ ];
/**
* @var Transaction the currently active transaction
*/
@@ -307,9 +310,7 @@ class Connection extends Component
Yii::endProfile($token, __METHOD__);
} catch (\PDOException $e) {
Yii::endProfile($token, __METHOD__);
- Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __METHOD__);
- $message = YII_DEBUG ? 'Failed to open DB connection: ' . $e->getMessage() : 'Failed to open DB connection.';
- throw new Exception($message, $e->errorInfo, (int)$e->getCode(), $e);
+ throw new Exception($e->getMessage(), $e->errorInfo, (int)$e->getCode(), $e);
}
}
}
@@ -360,7 +361,7 @@ class Connection extends Component
if ($this->emulatePrepare !== null && constant('PDO::ATTR_EMULATE_PREPARES')) {
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare);
}
- if ($this->charset !== null && in_array($this->getDriverName(), array('pgsql', 'mysql', 'mysqli'))) {
+ if ($this->charset !== null && in_array($this->getDriverName(), ['pgsql', 'mysql', 'mysqli', 'cubrid'])) {
$this->pdo->exec('SET NAMES ' . $this->pdo->quote($this->charset));
}
$this->trigger(self::EVENT_AFTER_OPEN);
@@ -372,13 +373,13 @@ class Connection extends Component
* @param array $params the parameters to be bound to the SQL statement
* @return Command the DB command
*/
- public function createCommand($sql = null, $params = array())
+ public function createCommand($sql = null, $params = [])
{
$this->open();
- $command = new Command(array(
+ $command = new Command([
'db' => $this,
'sql' => $sql,
- ));
+ ]);
return $command->bindValues($params);
}
@@ -398,9 +399,7 @@ class Connection extends Component
public function beginTransaction()
{
$this->open();
- $this->_transaction = new Transaction(array(
- 'db' => $this,
- ));
+ $this->_transaction = new Transaction(['db' => $this]);
$this->_transaction->begin();
return $this->_transaction;
}
@@ -499,19 +498,19 @@ class Connection extends Component
* Processes a SQL statement by quoting table and column names that are enclosed within double brackets.
* Tokens enclosed within double curly brackets are treated as table names, while
* tokens enclosed within double square brackets are column names. They will be quoted accordingly.
- * Also, the percentage character "%" in a table name will be replaced with [[tablePrefix]].
+ * Also, the percentage character "%" at the beginning or ending of a table name will be replaced
+ * with [[tablePrefix]].
* @param string $sql the SQL to be quoted
* @return string the quoted SQL
*/
public function quoteSql($sql)
{
- $db = $this;
- return preg_replace_callback('/(\\{\\{([%\w\-\. ]+)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/',
- function ($matches) use ($db) {
+ return preg_replace_callback('/(\\{\\{(%?[\w\-\. ]+%?)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/',
+ function ($matches) {
if (isset($matches[3])) {
- return $db->quoteColumnName($matches[3]);
+ return $this->quoteColumnName($matches[3]);
} else {
- return str_replace('%', $db->tablePrefix, $db->quoteTableName($matches[2]));
+ return str_replace('%', $this->tablePrefix, $this->quoteTableName($matches[2]));
}
}, $sql);
}
@@ -525,6 +524,7 @@ class Connection extends Component
if (($pos = strpos($this->dsn, ':')) !== false) {
return strtolower(substr($this->dsn, 0, $pos));
} else {
+ $this->open();
return strtolower($this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME));
}
}
diff --git a/framework/yii/db/DataReader.php b/framework/yii/db/DataReader.php
index ce776c0..213db52 100644
--- a/framework/yii/db/DataReader.php
+++ b/framework/yii/db/DataReader.php
@@ -39,10 +39,10 @@ use yii\base\InvalidCallException;
* [[fetchMode]]. See the [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)
* for more details about possible fetch mode.
*
- * @property boolean $isClosed whether the reader is closed or not.
- * @property integer $rowCount number of rows contained in the result.
- * @property integer $columnCount the number of columns in the result set.
- * @property mixed $fetchMode fetch mode used when retrieving the data.
+ * @property integer $columnCount The number of columns in the result set. This property is read-only.
+ * @property integer $fetchMode Fetch mode. This property is write-only.
+ * @property boolean $isClosed Whether the reader is closed or not. This property is read-only.
+ * @property integer $rowCount Number of rows contained in the result. This property is read-only.
*
* @author Qiang Xue
* @since 2.0
@@ -62,7 +62,7 @@ class DataReader extends \yii\base\Object implements \Iterator, \Countable
* @param Command $command the command generating the query result
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct(Command $command, $config = array())
+ public function __construct(Command $command, $config = [])
{
$this->_statement = $command->pdoStatement;
$this->_statement->setFetchMode(\PDO::FETCH_ASSOC);
@@ -73,7 +73,7 @@ class DataReader extends \yii\base\Object implements \Iterator, \Countable
* Binds a column to a PHP variable.
* When rows of data are being fetched, the corresponding column value
* will be set in the variable. Note, the fetch mode must include PDO::FETCH_BOUND.
- * @param mixed $column Number of the column (1-indexed) or name of the column
+ * @param integer|string $column Number of the column (1-indexed) or name of the column
* in the result set. If using the column name, be aware that the name
* should match the case of the column, as returned by the driver.
* @param mixed $value Name of the PHP variable to which the column will be bound.
@@ -91,13 +91,13 @@ class DataReader extends \yii\base\Object implements \Iterator, \Countable
/**
* Set the default fetch mode for this statement
- * @param mixed $mode fetch mode
+ * @param integer $mode fetch mode
* @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php
*/
public function setFetchMode($mode)
{
$params = func_get_args();
- call_user_func_array(array($this->_statement, 'setFetchMode'), $params);
+ call_user_func_array([$this->_statement, 'setFetchMode'], $params);
}
/**
@@ -112,7 +112,7 @@ class DataReader extends \yii\base\Object implements \Iterator, \Countable
/**
* Returns a single column from the next row of a result set.
* @param integer $columnIndex zero-based column index
- * @return mixed the column of the current row, false if no more row available
+ * @return mixed the column of the current row, false if no more rows available
*/
public function readColumn($columnIndex)
{
diff --git a/framework/yii/db/Exception.php b/framework/yii/db/Exception.php
index 9339211..f502043 100644
--- a/framework/yii/db/Exception.php
+++ b/framework/yii/db/Exception.php
@@ -16,19 +16,19 @@ namespace yii\db;
class Exception extends \yii\base\Exception
{
/**
- * @var mixed the error info provided by a PDO exception. This is the same as returned
+ * @var array the error info provided by a PDO exception. This is the same as returned
* by [PDO::errorInfo](http://www.php.net/manual/en/pdo.errorinfo.php).
*/
- public $errorInfo;
+ public $errorInfo = [];
/**
* Constructor.
* @param string $message PDO error message
- * @param mixed $errorInfo PDO error info
+ * @param array $errorInfo PDO error info
* @param integer $code PDO error code
* @param \Exception $previous The previous exception used for the exception chaining.
*/
- public function __construct($message, $errorInfo = null, $code = 0, \Exception $previous = null)
+ public function __construct($message, $errorInfo = [], $code = 0, \Exception $previous = null)
{
$this->errorInfo = $errorInfo;
parent::__construct($message, $code, $previous);
diff --git a/framework/yii/db/Expression.php b/framework/yii/db/Expression.php
index 77e9f60..7fa9124 100644
--- a/framework/yii/db/Expression.php
+++ b/framework/yii/db/Expression.php
@@ -34,7 +34,7 @@ class Expression extends \yii\base\Object
* The keys are placeholders appearing in [[expression]] and the values
* are the corresponding parameter values.
*/
- public $params = array();
+ public $params = [];
/**
* Constructor.
@@ -42,7 +42,7 @@ class Expression extends \yii\base\Object
* @param array $params parameters
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($expression, $params = array(), $config = array())
+ public function __construct($expression, $params = [], $config = [])
{
$this->expression = $expression;
$this->params = $params;
diff --git a/framework/yii/db/Migration.php b/framework/yii/db/Migration.php
index 774ac14..79a5abc 100644
--- a/framework/yii/db/Migration.php
+++ b/framework/yii/db/Migration.php
@@ -135,7 +135,7 @@ class Migration extends \yii\base\Component
* @param array $params input parameters (name => value) for the SQL execution.
* See [[Command::execute()]] for more details.
*/
- public function execute($sql, $params = array())
+ public function execute($sql, $params = [])
{
echo " > execute SQL: $sql ...";
$time = microtime(true);
@@ -158,15 +158,30 @@ class Migration extends \yii\base\Component
}
/**
+ * Creates and executes an batch INSERT SQL statement.
+ * The method will properly escape the column names, and bind the values to be inserted.
+ * @param string $table the table that new rows will be inserted into.
+ * @param array $columns the column names.
+ * @param array $rows the rows to be batch inserted into the table
+ */
+ public function batchInsert($table, $columns, $rows)
+ {
+ echo " > insert into $table ...";
+ $time = microtime(true);
+ $this->db->createCommand()->batchInsert($table, $columns, $rows)->execute();
+ echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
+ }
+
+ /**
* Creates and executes an UPDATE SQL statement.
* The method will properly escape the column names and bind the values to be updated.
* @param string $table the table to be updated.
* @param array $columns the column data (name => value) to be updated.
- * @param mixed $condition the conditions that will be put in the WHERE part. Please
+ * @param array|string $condition the conditions that will be put in the WHERE part. Please
* refer to [[Query::where()]] on how to specify conditions.
* @param array $params the parameters to be bound to the query.
*/
- public function update($table, $columns, $condition = '', $params = array())
+ public function update($table, $columns, $condition = '', $params = [])
{
echo " > update $table ...";
$time = microtime(true);
@@ -177,11 +192,11 @@ class Migration extends \yii\base\Component
/**
* Creates and executes a DELETE SQL statement.
* @param string $table the table where the data will be deleted from.
- * @param mixed $condition the conditions that will be put in the WHERE part. Please
+ * @param array|string $condition the conditions that will be put in the WHERE part. Please
* refer to [[Query::where()]] on how to specify conditions.
* @param array $params the parameters to be bound to the query.
*/
- public function delete($table, $condition = '', $params = array())
+ public function delete($table, $condition = '', $params = [])
{
echo " > delete from $table ...";
$time = microtime(true);
@@ -297,7 +312,7 @@ class Migration extends \yii\base\Component
* Builds and executes a SQL statement for changing the definition of a column.
* @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
* @param string $column the name of the column to be changed. The name will be properly quoted by the method.
- * @param string $type the new column type. The {@link getColumnType} method will be invoked to convert abstract column type (if any)
+ * @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract column type (if any)
* into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
* For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
*/
@@ -310,6 +325,35 @@ class Migration extends \yii\base\Component
}
/**
+ * Builds and executes a SQL statement for creating a primary key.
+ * The method will properly quote the table and column names.
+ * @param string $name the name of the primary key constraint.
+ * @param string $table the table that the primary key constraint will be added to.
+ * @param string|array $columns comma separated string or array of columns that the primary key will consist of.
+ */
+ public function addPrimaryKey($name, $table, $columns)
+ {
+ echo " > add primary key $name on $table (".(is_array($columns) ? implode(',', $columns) : $columns).") ...";
+ $time = microtime(true);
+ $this->db->createCommand()->addPrimaryKey($name, $table, $columns)->execute();
+ echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
+ }
+
+ /**
+ * Builds and executes a SQL statement for dropping a primary key.
+ * @param string $name the name of the primary key constraint to be removed.
+ * @param string $table the table that the primary key constraint will be removed from.
+ * @return Command the command object itself
+ */
+ public function dropPrimaryKey($name, $table)
+ {
+ echo " > drop primary key $name ...";
+ $time = microtime(true);
+ $this->db->createCommand()->dropPrimaryKey($name, $table)->execute();
+ echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
+ }
+
+ /**
* Builds a SQL statement for adding a foreign key constraint to an existing table.
* The method will properly quote the table and column names.
* @param string $name the name of the foreign key constraint.
diff --git a/framework/yii/db/Query.php b/framework/yii/db/Query.php
index 2e03949..ee24c2f 100644
--- a/framework/yii/db/Query.php
+++ b/framework/yii/db/Query.php
@@ -7,6 +7,9 @@
namespace yii\db;
+use Yii;
+use yii\base\Component;
+
/**
* Query represents a SELECT SQL statement in a way that is independent of DBMS.
*
@@ -20,34 +23,29 @@ namespace yii\db;
*
* ~~~
* $query = new Query;
+ * // compose the query
* $query->select('id, name')
* ->from('tbl_user')
* ->limit(10);
* // build and execute the query
+ * $rows = $query->all();
+ * // alternatively, you can create DB command and execute it
* $command = $query->createCommand();
* // $command->sql returns the actual SQL
* $rows = $command->queryAll();
* ~~~
*
* @author Qiang Xue
+ * @author Carsten Brandt
* @since 2.0
*/
-class Query extends \yii\base\Component
+class Query extends Component implements QueryInterface
{
- /**
- * Sort ascending
- * @see orderBy
- */
- const SORT_ASC = false;
- /**
- * Sort ascending
- * @see orderBy
- */
- const SORT_DESC = true;
+ use QueryTrait;
/**
- * @var array the columns being selected. For example, `array('id', 'name')`.
- * This is used to construct the SELECT clause in a SQL statement. If not set, if means selecting all columns.
+ * @var array the columns being selected. For example, `['id', 'name']`.
+ * This is used to construct the SELECT clause in a SQL statement. If not set, it means selecting all columns.
* @see select()
*/
public $select;
@@ -62,35 +60,13 @@ class Query extends \yii\base\Component
*/
public $distinct;
/**
- * @var array the table(s) to be selected from. For example, `array('tbl_user', 'tbl_post')`.
+ * @var array the table(s) to be selected from. For example, `['tbl_user', 'tbl_post']`.
* This is used to construct the FROM clause in a SQL statement.
* @see from()
*/
public $from;
/**
- * @var string|array query condition. This refers to the WHERE clause in a SQL statement.
- * For example, `age > 31 AND team = 1`.
- * @see where()
- */
- public $where;
- /**
- * @var integer maximum number of records to be returned. If not set or less than 0, it means no limit.
- */
- public $limit;
- /**
- * @var integer zero-based offset from where the records are to be returned. If not set or
- * less than 0, it means starting from the beginning.
- */
- public $offset;
- /**
- * @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
- * The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
- * can be either [[Query::SORT_ASC]] or [[Query::SORT_DESC]]. The array may also contain [[Expression]] objects.
- * If that is the case, the expressions will be converted into strings without any change.
- */
- public $orderBy;
- /**
- * @var array how to group the query results. For example, `array('company', 'department')`.
+ * @var array how to group the query results. For example, `['company', 'department']`.
* This is used to construct the GROUP BY clause in a SQL statement.
*/
public $groupBy;
@@ -99,16 +75,16 @@ class Query extends \yii\base\Component
* of one join which has the following structure:
*
* ~~~
- * array($joinType, $tableName, $joinCondition)
+ * [$joinType, $tableName, $joinCondition]
* ~~~
*
* For example,
*
* ~~~
- * array(
- * array('INNER JOIN', 'tbl_user', 'tbl_user.id = author_id'),
- * array('LEFT JOIN', 'tbl_team', 'tbl_team.id = team_id'),
- * )
+ * [
+ * ['INNER JOIN', 'tbl_user', 'tbl_user.id = author_id'],
+ * ['LEFT JOIN', 'tbl_team', 'tbl_team.id = team_id'],
+ * ]
* ~~~
*/
public $join;
@@ -124,10 +100,11 @@ class Query extends \yii\base\Component
public $union;
/**
* @var array list of query parameter values indexed by parameter placeholders.
- * For example, `array(':name' => 'Dan', ':age' => 31)`.
+ * For example, `[':name' => 'Dan', ':age' => 31]`.
*/
public $params;
+
/**
* Creates a DB command that can be used to execute this query.
* @param Connection $db the database connection used to generate the SQL statement.
@@ -137,22 +114,191 @@ class Query extends \yii\base\Component
public function createCommand($db = null)
{
if ($db === null) {
- $db = \Yii::$app->db;
+ $db = Yii::$app->getDb();
+ }
+ list ($sql, $params) = $db->getQueryBuilder()->build($this);
+ return $db->createCommand($sql, $params);
+ }
+
+ /**
+ * Executes the query and returns all results as an array.
+ * @param Connection $db the database connection used to generate the SQL statement.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return array the query results. If the query results in nothing, an empty array will be returned.
+ */
+ public function all($db = null)
+ {
+ $rows = $this->createCommand($db)->queryAll();
+ if ($this->indexBy === null) {
+ return $rows;
+ }
+ $result = [];
+ foreach ($rows as $row) {
+ if (is_string($this->indexBy)) {
+ $key = $row[$this->indexBy];
+ } else {
+ $key = call_user_func($this->indexBy, $row);
+ }
+ $result[$key] = $row;
}
- $sql = $db->getQueryBuilder()->build($this);
- return $db->createCommand($sql, $this->params);
+ return $result;
+ }
+
+ /**
+ * Executes the query and returns a single row of result.
+ * @param Connection $db the database connection used to generate the SQL statement.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query
+ * results in nothing.
+ */
+ public function one($db = null)
+ {
+ return $this->createCommand($db)->queryOne();
+ }
+
+ /**
+ * Returns the query result as a scalar value.
+ * The value returned will be the first column in the first row of the query results.
+ * @param Connection $db the database connection used to generate the SQL statement.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return string|boolean the value of the first column in the first row of the query result.
+ * False is returned if the query result is empty.
+ */
+ public function scalar($db = null)
+ {
+ return $this->createCommand($db)->queryScalar();
+ }
+
+ /**
+ * Executes the query and returns the first column of the result.
+ * @param Connection $db the database connection used to generate the SQL statement.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return array the first column of the query result. An empty array is returned if the query results in nothing.
+ */
+ public function column($db = null)
+ {
+ return $this->createCommand($db)->queryColumn();
+ }
+
+ /**
+ * Returns the number of records.
+ * @param string $q the COUNT expression. Defaults to '*'.
+ * Make sure you properly quote column names in the expression.
+ * @param Connection $db the database connection used to generate the SQL statement.
+ * If this parameter is not given (or null), the `db` application component will be used.
+ * @return integer number of records
+ */
+ public function count($q = '*', $db = null)
+ {
+ return $this->queryScalar("COUNT($q)", $db);
+ }
+
+ /**
+ * Returns the sum of the specified column values.
+ * @param string $q the column name or expression.
+ * Make sure you properly quote column names in the expression.
+ * @param Connection $db the database connection used to generate the SQL statement.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return integer the sum of the specified column values
+ */
+ public function sum($q, $db = null)
+ {
+ return $this->queryScalar("SUM($q)", $db);
+ }
+
+ /**
+ * Returns the average of the specified column values.
+ * @param string $q the column name or expression.
+ * Make sure you properly quote column names in the expression.
+ * @param Connection $db the database connection used to generate the SQL statement.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return integer the average of the specified column values.
+ */
+ public function average($q, $db = null)
+ {
+ return $this->queryScalar("AVG($q)", $db);
+ }
+
+ /**
+ * Returns the minimum of the specified column values.
+ * @param string $q the column name or expression.
+ * Make sure you properly quote column names in the expression.
+ * @param Connection $db the database connection used to generate the SQL statement.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return integer the minimum of the specified column values.
+ */
+ public function min($q, $db = null)
+ {
+ return $this->queryScalar("MIN($q)", $db);
+ }
+
+ /**
+ * Returns the maximum of the specified column values.
+ * @param string $q the column name or expression.
+ * Make sure you properly quote column names in the expression.
+ * @param Connection $db the database connection used to generate the SQL statement.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return integer the maximum of the specified column values.
+ */
+ public function max($q, $db = null)
+ {
+ return $this->queryScalar("MAX($q)", $db);
+ }
+
+ /**
+ * Returns a value indicating whether the query result contains any row of data.
+ * @param Connection $db the database connection used to generate the SQL statement.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return boolean whether the query result contains any row of data.
+ */
+ public function exists($db = null)
+ {
+ $select = $this->select;
+ $this->select = [new Expression('1')];
+ $command = $this->createCommand($db);
+ $this->select = $select;
+ return $command->queryScalar() !== false;
+ }
+
+ /**
+ * Queries a scalar value by setting [[select]] first.
+ * Restores the value of select to make this query reusable.
+ * @param string|Expression $selectExpression
+ * @param Connection $db
+ * @return bool|string
+ */
+ private function queryScalar($selectExpression, $db)
+ {
+ $select = $this->select;
+ $limit = $this->limit;
+ $offset = $this->offset;
+
+ $this->select = [$selectExpression];
+ $this->limit = null;
+ $this->offset = null;
+ $command = $this->createCommand($db);
+
+ $this->select = $select;
+ $this->limit = $limit;
+ $this->offset = $offset;
+
+ return $command->queryScalar();
}
/**
* Sets the SELECT part of the query.
* @param string|array $columns the columns to be selected.
- * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
+ * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
* Columns can contain table prefixes (e.g. "tbl_user.id") and/or column aliases (e.g. "tbl_user.id AS user_id").
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
+ *
+ * Note that if you are selecting an expression like `CONCAT(first_name, ' ', last_name)`, you should
+ * use an array to specify the columns. Otherwise, the expression may be incorrectly split into several parts.
+ *
* @param string $option additional option that should be appended to the 'SELECT' keyword. For example,
* in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
- * @return Query the query object itself
+ * @return static the query object itself
*/
public function select($columns, $option = null)
{
@@ -167,7 +313,7 @@ class Query extends \yii\base\Component
/**
* Sets the value indicating whether to SELECT DISTINCT or not.
* @param bool $value whether to SELECT DISTINCT or not.
- * @return Query the query object itself
+ * @return static the query object itself
*/
public function distinct($value = true)
{
@@ -178,11 +324,11 @@ class Query extends \yii\base\Component
/**
* Sets the FROM part of the query.
* @param string|array $tables the table(s) to be selected from. This can be either a string (e.g. `'tbl_user'`)
- * or an array (e.g. `array('tbl_user', 'tbl_profile')`) specifying one or several table names.
+ * or an array (e.g. `['tbl_user', 'tbl_profile']`) specifying one or several table names.
* Table names can contain schema prefixes (e.g. `'public.tbl_user'`) and/or table aliases (e.g. `'tbl_user u'`).
* The method will automatically quote the table names unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
- * @return Query the query object itself
+ * @return static the query object itself
*/
public function from($tables)
{
@@ -202,48 +348,48 @@ class Query extends \yii\base\Component
* The $condition parameter should be either a string (e.g. 'id=1') or an array.
* If the latter, it must be in one of the following two formats:
*
- * - hash format: `array('column1' => value1, 'column2' => value2, ...)`
- * - operator format: `array(operator, operand1, operand2, ...)`
+ * - hash format: `['column1' => value1, 'column2' => value2, ...]`
+ * - operator format: `[operator, operand1, operand2, ...]`
*
* A condition in hash format represents the following SQL expression in general:
* `column1=value1 AND column2=value2 AND ...`. In case when a value is an array,
* an `IN` expression will be generated. And if a value is null, `IS NULL` will be used
* in the generated expression. Below are some examples:
*
- * - `array('type' => 1, 'status' => 2)` generates `(type = 1) AND (status = 2)`.
- * - `array('id' => array(1, 2, 3), 'status' => 2)` generates `(id IN (1, 2, 3)) AND (status = 2)`.
- * - `array('status' => null) generates `status IS NULL`.
+ * - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`.
+ * - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`.
+ * - `['status' => null] generates `status IS NULL`.
*
* A condition in operator format generates the SQL expression according to the specified operator, which
* can be one of the followings:
*
* - `and`: the operands should be concatenated together using `AND`. For example,
- * `array('and', 'id=1', 'id=2')` will generate `id=1 AND id=2`. If an operand is an array,
+ * `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array,
* it will be converted into a string using the rules described here. For example,
- * `array('and', 'type=1', array('or', 'id=1', 'id=2'))` will generate `type=1 AND (id=1 OR id=2)`.
+ * `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`.
* The method will NOT do any quoting or escaping.
*
* - `or`: similar to the `and` operator except that the operands are concatenated using `OR`.
*
* - `between`: operand 1 should be the column name, and operand 2 and 3 should be the
* starting and ending values of the range that the column is in.
- * For example, `array('between', 'id', 1, 10)` will generate `id BETWEEN 1 AND 10`.
+ * For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`.
*
* - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
* in the generated condition.
*
* - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing
* the range of the values that the column or DB expression should be in. For example,
- * `array('in', 'id', array(1, 2, 3))` will generate `id IN (1, 2, 3)`.
+ * `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`.
* The method will properly quote the column name and escape values in the range.
*
* - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
*
* - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
* the values that the column or DB expression should be like.
- * For example, `array('like', 'name', '%tester%')` will generate `name LIKE '%tester%'`.
+ * For example, `['like', 'name', '%tester%']` will generate `name LIKE '%tester%'`.
* When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
- * using `AND`. For example, `array('like', 'name', array('%test%', '%sample%'))` will generate
+ * using `AND`. For example, `['like', 'name', ['%test%', '%sample%']]` will generate
* `name LIKE '%test%' AND name LIKE '%sample%'`.
* The method will properly quote the column name and escape values in the range.
*
@@ -258,11 +404,11 @@ class Query extends \yii\base\Component
*
* @param string|array $condition the conditions that should be put in the WHERE part.
* @param array $params the parameters (name => value) to be bound to the query.
- * @return Query the query object itself
+ * @return static the query object itself
* @see andWhere()
* @see orWhere()
*/
- public function where($condition, $params = array())
+ public function where($condition, $params = [])
{
$this->where = $condition;
$this->addParams($params);
@@ -275,16 +421,16 @@ class Query extends \yii\base\Component
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
- * @return Query the query object itself
+ * @return static the query object itself
* @see where()
* @see orWhere()
*/
- public function andWhere($condition, $params = array())
+ public function andWhere($condition, $params = [])
{
if ($this->where === null) {
$this->where = $condition;
} else {
- $this->where = array('and', $this->where, $condition);
+ $this->where = ['and', $this->where, $condition];
}
$this->addParams($params);
return $this;
@@ -296,16 +442,16 @@ class Query extends \yii\base\Component
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
- * @return Query the query object itself
+ * @return static the query object itself
* @see where()
* @see andWhere()
*/
- public function orWhere($condition, $params = array())
+ public function orWhere($condition, $params = [])
{
if ($this->where === null) {
$this->where = $condition;
} else {
- $this->where = array('or', $this->where, $condition);
+ $this->where = ['or', $this->where, $condition];
}
$this->addParams($params);
return $this;
@@ -324,9 +470,9 @@ class Query extends \yii\base\Component
* @param array $params the parameters (name => value) to be bound to the query.
* @return Query the query object itself
*/
- public function join($type, $table, $on = '', $params = array())
+ public function join($type, $table, $on = '', $params = [])
{
- $this->join[] = array($type, $table, $on);
+ $this->join[] = [$type, $table, $on];
return $this->addParams($params);
}
@@ -341,9 +487,9 @@ class Query extends \yii\base\Component
* @param array $params the parameters (name => value) to be bound to the query.
* @return Query the query object itself
*/
- public function innerJoin($table, $on = '', $params = array())
+ public function innerJoin($table, $on = '', $params = [])
{
- $this->join[] = array('INNER JOIN', $table, $on);
+ $this->join[] = ['INNER JOIN', $table, $on];
return $this->addParams($params);
}
@@ -358,9 +504,9 @@ class Query extends \yii\base\Component
* @param array $params the parameters (name => value) to be bound to the query
* @return Query the query object itself
*/
- public function leftJoin($table, $on = '', $params = array())
+ public function leftJoin($table, $on = '', $params = [])
{
- $this->join[] = array('LEFT JOIN', $table, $on);
+ $this->join[] = ['LEFT JOIN', $table, $on];
return $this->addParams($params);
}
@@ -375,19 +521,19 @@ class Query extends \yii\base\Component
* @param array $params the parameters (name => value) to be bound to the query
* @return Query the query object itself
*/
- public function rightJoin($table, $on = '', $params = array())
+ public function rightJoin($table, $on = '', $params = [])
{
- $this->join[] = array('RIGHT JOIN', $table, $on);
+ $this->join[] = ['RIGHT JOIN', $table, $on];
return $this->addParams($params);
}
/**
* Sets the GROUP BY part of the query.
* @param string|array $columns the columns to be grouped by.
- * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
+ * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
- * @return Query the query object itself
+ * @return static the query object itself
* @see addGroupBy()
*/
public function groupBy($columns)
@@ -402,10 +548,10 @@ class Query extends \yii\base\Component
/**
* Adds additional group-by columns to the existing ones.
* @param string|array $columns additional columns to be grouped by.
- * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
+ * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
- * @return Query the query object itself
+ * @return static the query object itself
* @see groupBy()
*/
public function addGroupBy($columns)
@@ -426,11 +572,11 @@ class Query extends \yii\base\Component
* @param string|array $condition the conditions to be put after HAVING.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
- * @return Query the query object itself
+ * @return static the query object itself
* @see andHaving()
* @see orHaving()
*/
- public function having($condition, $params = array())
+ public function having($condition, $params = [])
{
$this->having = $condition;
$this->addParams($params);
@@ -443,16 +589,16 @@ class Query extends \yii\base\Component
* @param string|array $condition the new HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
- * @return Query the query object itself
+ * @return static the query object itself
* @see having()
* @see orHaving()
*/
- public function andHaving($condition, $params = array())
+ public function andHaving($condition, $params = [])
{
if ($this->having === null) {
$this->having = $condition;
} else {
- $this->having = array('and', $this->having, $condition);
+ $this->having = ['and', $this->having, $condition];
}
$this->addParams($params);
return $this;
@@ -464,102 +610,25 @@ class Query extends \yii\base\Component
* @param string|array $condition the new HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
- * @return Query the query object itself
+ * @return static the query object itself
* @see having()
* @see andHaving()
*/
- public function orHaving($condition, $params = array())
+ public function orHaving($condition, $params = [])
{
if ($this->having === null) {
$this->having = $condition;
} else {
- $this->having = array('or', $this->having, $condition);
+ $this->having = ['or', $this->having, $condition];
}
$this->addParams($params);
return $this;
}
/**
- * Sets the ORDER BY part of the query.
- * @param string|array $columns the columns (and the directions) to be ordered by.
- * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
- * (e.g. `array('id' => Query::SORT_ASC, 'name' => Query::SORT_DESC)`).
- * The method will automatically quote the column names unless a column contains some parenthesis
- * (which means the column contains a DB expression).
- * @return Query the query object itself
- * @see addOrderBy()
- */
- public function orderBy($columns)
- {
- $this->orderBy = $this->normalizeOrderBy($columns);
- return $this;
- }
-
- /**
- * Adds additional ORDER BY columns to the query.
- * @param string|array $columns the columns (and the directions) to be ordered by.
- * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
- * (e.g. `array('id' => Query::SORT_ASC, 'name' => Query::SORT_DESC)`).
- * The method will automatically quote the column names unless a column contains some parenthesis
- * (which means the column contains a DB expression).
- * @return Query the query object itself
- * @see orderBy()
- */
- public function addOrderBy($columns)
- {
- $columns = $this->normalizeOrderBy($columns);
- if ($this->orderBy === null) {
- $this->orderBy = $columns;
- } else {
- $this->orderBy = array_merge($this->orderBy, $columns);
- }
- return $this;
- }
-
- protected function normalizeOrderBy($columns)
- {
- if (is_array($columns)) {
- return $columns;
- } else {
- $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
- $result = array();
- foreach ($columns as $column) {
- if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
- $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? self::SORT_ASC : self::SORT_DESC;
- } else {
- $result[$column] = self::SORT_ASC;
- }
- }
- return $result;
- }
- }
-
- /**
- * Sets the LIMIT part of the query.
- * @param integer $limit the limit
- * @return Query the query object itself
- */
- public function limit($limit)
- {
- $this->limit = $limit;
- return $this;
- }
-
- /**
- * Sets the OFFSET part of the query.
- * @param integer $offset the offset
- * @return Query the query object itself
- */
- public function offset($offset)
- {
- $this->offset = $offset;
- return $this;
- }
-
- /**
* Appends a SQL statement using UNION operator.
* @param string|Query $sql the SQL statement to be appended using UNION
- * @return Query the query object itself
+ * @return static the query object itself
*/
public function union($sql)
{
@@ -570,8 +639,8 @@ class Query extends \yii\base\Component
/**
* Sets the parameters to be bound to the query.
* @param array $params list of query parameter values indexed by parameter placeholders.
- * For example, `array(':name' => 'Dan', ':age' => 31)`.
- * @return Query the query object itself
+ * For example, `[':name' => 'Dan', ':age' => 31]`.
+ * @return static the query object itself
* @see addParams()
*/
public function params($params)
@@ -583,8 +652,8 @@ class Query extends \yii\base\Component
/**
* Adds additional parameters to be bound to the query.
* @param array $params list of query parameter values indexed by parameter placeholders.
- * For example, `array(':name' => 'Dan', ':age' => 31)`.
- * @return Query the query object itself
+ * For example, `[':name' => 'Dan', ':age' => 31]`.
+ * @return static the query object itself
* @see params()
*/
public function addParams($params)
diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php
index 04f1969..a76f211 100644
--- a/framework/yii/db/QueryBuilder.php
+++ b/framework/yii/db/QueryBuilder.php
@@ -7,7 +7,7 @@
namespace yii\db;
-use yii\db\Exception;
+use yii\base\InvalidParamException;
use yii\base\NotSupportedException;
/**
@@ -40,14 +40,14 @@ class QueryBuilder extends \yii\base\Object
* This is mainly used to support creating/modifying tables using DB-independent data type specifications.
* Child classes should override this property to declare supported type mappings.
*/
- public $typeMap = array();
+ public $typeMap = [];
/**
* Constructor.
* @param Connection $connection the database connection.
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($connection, $config = array())
+ public function __construct($connection, $config = [])
{
$this->db = $connection;
parent::__construct($config);
@@ -56,22 +56,24 @@ class QueryBuilder extends \yii\base\Object
/**
* Generates a SELECT SQL statement from a [[Query]] object.
* @param Query $query the [[Query]] object from which the SQL statement will be generated
- * @return string the generated SQL statement
+ * @return array the generated SQL statement (the first array element) and the corresponding
+ * parameters to be bound to the SQL statement (the second array element).
*/
public function build($query)
{
- $clauses = array(
+ $params = $query->params;
+ $clauses = [
$this->buildSelect($query->select, $query->distinct, $query->selectOption),
$this->buildFrom($query->from),
- $this->buildJoin($query->join, $query->params),
- $this->buildWhere($query->where, $query->params),
+ $this->buildJoin($query->join, $params),
+ $this->buildWhere($query->where, $params),
$this->buildGroupBy($query->groupBy),
- $this->buildHaving($query->having, $query->params),
- $this->buildUnion($query->union, $query->params),
+ $this->buildHaving($query->having, $params),
+ $this->buildUnion($query->union, $params),
$this->buildOrderBy($query->orderBy),
$this->buildLimit($query->limit, $query->offset),
- );
- return implode($this->separator, array_filter($clauses));
+ ];
+ return [implode($this->separator, array_filter($clauses)), $params];
}
/**
@@ -79,10 +81,10 @@ class QueryBuilder extends \yii\base\Object
* For example,
*
* ~~~
- * $sql = $queryBuilder->insert('tbl_user', array(
+ * $sql = $queryBuilder->insert('tbl_user', [
* 'name' => 'Sam',
* 'age' => 30,
- * ), $params);
+ * ], $params);
* ~~~
*
* The method will properly escape the table and column names.
@@ -95,8 +97,13 @@ class QueryBuilder extends \yii\base\Object
*/
public function insert($table, $columns, &$params)
{
- $names = array();
- $placeholders = array();
+ if (($tableSchema = $this->db->getTableSchema($table)) !== null) {
+ $columnSchemas = $tableSchema->columns;
+ } else {
+ $columnSchemas = [];
+ }
+ $names = [];
+ $placeholders = [];
foreach ($columns as $name => $value) {
$names[] = $this->db->quoteColumnName($name);
if ($value instanceof Expression) {
@@ -107,7 +114,7 @@ class QueryBuilder extends \yii\base\Object
} else {
$phName = self::PARAM_PREFIX . count($params);
$placeholders[] = $phName;
- $params[$phName] = $value;
+ $params[$phName] = !is_array($value) && isset($columnSchemas[$name]) ? $columnSchemas[$name]->typecast($value) : $value;
}
}
@@ -121,25 +128,46 @@ class QueryBuilder extends \yii\base\Object
* For example,
*
* ~~~
- * $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
- * array('Tom', 30),
- * array('Jane', 20),
- * array('Linda', 25),
- * ))->execute();
+ * $connection->createCommand()->batchInsert('tbl_user', ['name', 'age'], [
+ * ['Tom', 30],
+ * ['Jane', 20],
+ * ['Linda', 25],
+ * ])->execute();
* ~~~
*
- * Not that the values in each row must match the corresponding column names.
+ * Note that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
* @param array $rows the rows to be batch inserted into the table
* @return string the batch INSERT SQL statement
- * @throws NotSupportedException if this is not supported by the underlying DBMS
*/
public function batchInsert($table, $columns, $rows)
{
- throw new NotSupportedException($this->db->getDriverName() . ' does not support batch insert.');
+ if (($tableSchema = $this->db->getTableSchema($table)) !== null) {
+ $columnSchemas = $tableSchema->columns;
+ } else {
+ $columnSchemas = [];
+ }
+
+ foreach ($columns as $i => $name) {
+ $columns[$i] = $this->db->quoteColumnName($name);
+ }
+
+ $values = [];
+ foreach ($rows as $row) {
+ $vs = [];
+ foreach ($row as $i => $value) {
+ if (!is_array($value) && isset($columnSchemas[$columns[$i]])) {
+ $value = $columnSchemas[$columns[$i]]->typecast($value);
+ }
+ $vs[] = is_string($value) ? $this->db->quoteValue($value) : $value;
+ }
+ $values[] = '(' . implode(', ', $vs) . ')';
+ }
+ return 'INSERT INTO ' . $this->db->quoteTableName($table)
+ . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);
}
/**
@@ -147,17 +175,15 @@ class QueryBuilder extends \yii\base\Object
* For example,
*
* ~~~
- * $params = array();
- * $sql = $queryBuilder->update('tbl_user', array(
- * 'status' => 1,
- * ), 'age > 30', $params);
+ * $params = [];
+ * $sql = $queryBuilder->update('tbl_user', ['status' => 1], 'age > 30', $params);
* ~~~
*
* The method will properly escape the table and column names.
*
* @param string $table the table to be updated.
* @param array $columns the column data (name => value) to be updated.
- * @param mixed $condition the condition that will be put in the WHERE part. Please
+ * @param array|string $condition the condition that will be put in the WHERE part. Please
* refer to [[Query::where()]] on how to specify condition.
* @param array $params the binding parameters that will be modified by this method
* so that they can be bound to the DB command later.
@@ -165,7 +191,13 @@ class QueryBuilder extends \yii\base\Object
*/
public function update($table, $columns, $condition, &$params)
{
- $lines = array();
+ if (($tableSchema = $this->db->getTableSchema($table)) !== null) {
+ $columnSchemas = $tableSchema->columns;
+ } else {
+ $columnSchemas = [];
+ }
+
+ $lines = [];
foreach ($columns as $name => $value) {
if ($value instanceof Expression) {
$lines[] = $this->db->quoteColumnName($name) . '=' . $value->expression;
@@ -175,7 +207,7 @@ class QueryBuilder extends \yii\base\Object
} else {
$phName = self::PARAM_PREFIX . count($params);
$lines[] = $this->db->quoteColumnName($name) . '=' . $phName;
- $params[$phName] = $value;
+ $params[$phName] = !is_array($value) && isset($columnSchemas[$name]) ? $columnSchemas[$name]->typecast($value) : $value;
}
}
@@ -195,7 +227,7 @@ class QueryBuilder extends \yii\base\Object
* The method will properly escape the table and column names.
*
* @param string $table the table where the data will be deleted from.
- * @param mixed $condition the condition that will be put in the WHERE part. Please
+ * @param array|string $condition the condition that will be put in the WHERE part. Please
* refer to [[Query::where()]] on how to specify condition.
* @param array $params the binding parameters that will be modified by this method
* so that they can be bound to the DB command later.
@@ -222,11 +254,11 @@ class QueryBuilder extends \yii\base\Object
* For example,
*
* ~~~
- * $sql = $queryBuilder->createTable('tbl_user', array(
+ * $sql = $queryBuilder->createTable('tbl_user', [
* 'id' => 'pk',
* 'name' => 'string',
* 'age' => 'integer',
- * ));
+ * ]);
* ~~~
*
* @param string $table the name of the table to be created. The name will be properly quoted by the method.
@@ -236,7 +268,7 @@ class QueryBuilder extends \yii\base\Object
*/
public function createTable($table, $columns, $options = null)
{
- $cols = array();
+ $cols = [];
foreach ($columns as $name => $type) {
if (is_string($name)) {
$cols[] = "\t" . $this->db->quoteColumnName($name) . ' ' . $this->getColumnType($type);
@@ -270,6 +302,40 @@ class QueryBuilder extends \yii\base\Object
}
/**
+ * Builds a SQL statement for adding a primary key constraint to an existing table.
+ * @param string $name the name of the primary key constraint.
+ * @param string $table the table that the primary key constraint will be added to.
+ * @param string|array $columns comma separated string or array of columns that the primary key will consist of.
+ * @return string the SQL statement for adding a primary key constraint to an existing table.
+ */
+ public function addPrimaryKey($name, $table, $columns)
+ {
+ if (is_string($columns)) {
+ $columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY);
+ }
+
+ foreach ($columns as $i => $col) {
+ $columns[$i] = $this->db->quoteColumnName($col);
+ }
+
+ return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT '
+ . $this->db->quoteColumnName($name) . ' PRIMARY KEY ('
+ . implode(', ', $columns). ' )';
+ }
+
+ /**
+ * Builds a SQL statement for removing a primary key constraint to an existing table.
+ * @param string $name the name of the primary key constraint to be removed.
+ * @param string $table the table that the primary key constraint will be removed from.
+ * @return string the SQL statement for removing a primary key constraint from an existing table. *
+ */
+ public function dropPrimaryKey($name, $table)
+ {
+ return 'ALTER TABLE ' . $this->db->quoteTableName($table)
+ . ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);
+ }
+
+ /**
* Builds a SQL statement for truncating a DB table.
* @param string $table the table to be truncated. The name will be properly quoted by the method.
* @return string the SQL statement for truncating a DB table.
@@ -415,7 +481,7 @@ class QueryBuilder extends \yii\base\Object
* The sequence will be reset such that the primary key of the next new row inserted
* will have the specified value or 1.
* @param string $table the name of the table whose primary key sequence will be reset
- * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
+ * @param array|string $value the value for the primary key of the next new row inserted. If this is not set,
* the next new row's primary key will have a value 1.
* @return string the SQL statement for resetting sequence
* @throws NotSupportedException if this is not supported by the underlying DBMS
@@ -445,6 +511,7 @@ class QueryBuilder extends \yii\base\Object
* physical types):
*
* - `pk`: an auto-incremental primary key type, will be converted into "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"
+ * - `bigpk`: an auto-incremental primary key type, will be converted into "bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY"
* - `string`: string type, will be converted into "varchar(255)"
* - `text`: a long string type, will be converted into "text"
* - `smallint`: a small integer type, will be converted into "smallint(6)"
@@ -538,7 +605,7 @@ class QueryBuilder extends \yii\base\Object
foreach ($tables as $i => $table) {
if (strpos($table, '(') === false) {
- if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias
+ if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) { // with alias
$tables[$i] = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
} else {
$tables[$i] = $this->db->quoteTableName($table);
@@ -572,7 +639,7 @@ class QueryBuilder extends \yii\base\Object
// 0:join type, 1:table name, 2:on-condition
$table = $join[1];
if (strpos($table, '(') === false) {
- if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias
+ if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) { // with alias
$table = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
} else {
$table = $this->db->quoteTableName($table);
@@ -633,12 +700,12 @@ class QueryBuilder extends \yii\base\Object
if (empty($columns)) {
return '';
}
- $orders = array();
+ $orders = [];
foreach ($columns as $name => $direction) {
if (is_object($direction)) {
$orders[] = (string)$direction;
} else {
- $orders[] = $this->db->quoteColumnName($name) . ($direction === Query::SORT_DESC ? ' DESC' : '');
+ $orders[] = $this->db->quoteColumnName($name) . ($direction === SORT_DESC ? ' DESC' : '');
}
}
@@ -674,9 +741,11 @@ class QueryBuilder extends \yii\base\Object
}
foreach ($unions as $i => $union) {
if ($union instanceof Query) {
+ // save the original parameters so that we can restore them later to prevent from modifying the query object
+ $originalParams = $union->params;
$union->addParams($params);
- $unions[$i] = $this->build($union);
- $params = $union->params;
+ list ($unions[$i], $params) = $this->build($union);
+ $union->params = $originalParams;
}
}
return "UNION (\n" . implode("\n) UNION (\n", $unions) . "\n)";
@@ -714,11 +783,12 @@ class QueryBuilder extends \yii\base\Object
* on how to specify a condition.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
- * @throws \yii\db\Exception if the condition is in bad format
+ * @throws InvalidParamException if the condition is in bad format
*/
public function buildCondition($condition, &$params)
{
- static $builders = array(
+ static $builders = [
+ 'NOT' => 'buildNotCondition',
'AND' => 'buildAndCondition',
'OR' => 'buildAndCondition',
'BETWEEN' => 'buildBetweenCondition',
@@ -729,7 +799,7 @@ class QueryBuilder extends \yii\base\Object
'NOT LIKE' => 'buildLikeCondition',
'OR LIKE' => 'buildLikeCondition',
'OR NOT LIKE' => 'buildLikeCondition',
- );
+ ];
if (!is_array($condition)) {
return (string)$condition;
@@ -743,19 +813,25 @@ class QueryBuilder extends \yii\base\Object
array_shift($condition);
return $this->$method($operator, $condition, $params);
} else {
- throw new Exception('Found unknown operator in query: ' . $operator);
+ throw new InvalidParamException('Found unknown operator in query: ' . $operator);
}
} else { // hash format: 'column1' => 'value1', 'column2' => 'value2', ...
return $this->buildHashCondition($condition, $params);
}
}
- private function buildHashCondition($condition, &$params)
+ /**
+ * Creates a condition based on column-value pairs.
+ * @param array $condition the condition specification.
+ * @param array $params the binding parameters to be populated
+ * @return string the generated SQL expression
+ */
+ public function buildHashCondition($condition, &$params)
{
- $parts = array();
+ $parts = [];
foreach ($condition as $column => $value) {
if (is_array($value)) { // IN condition
- $parts[] = $this->buildInCondition('in', array($column, $value), $params);
+ $parts[] = $this->buildInCondition('IN', [$column, $value], $params);
} else {
if (strpos($column, '(') === false) {
$column = $this->db->quoteColumnName($column);
@@ -777,9 +853,16 @@ class QueryBuilder extends \yii\base\Object
return count($parts) === 1 ? $parts[0] : '(' . implode(') AND (', $parts) . ')';
}
- private function buildAndCondition($operator, $operands, &$params)
+ /**
+ * Connects two or more SQL expressions with the `AND` or `OR` operator.
+ * @param string $operator the operator to use for connecting the given operands
+ * @param array $operands the SQL expressions to connect.
+ * @param array $params the binding parameters to be populated
+ * @return string the generated SQL expression
+ */
+ public function buildAndCondition($operator, $operands, &$params)
{
- $parts = array();
+ $parts = [];
foreach ($operands as $operand) {
if (is_array($operand)) {
$operand = $this->buildCondition($operand, $params);
@@ -795,10 +878,43 @@ class QueryBuilder extends \yii\base\Object
}
}
- private function buildBetweenCondition($operator, $operands, &$params)
+ /**
+ * Inverts an SQL expressions with `NOT` operator.
+ * @param string $operator the operator to use for connecting the given operands
+ * @param array $operands the SQL expressions to connect.
+ * @param array $params the binding parameters to be populated
+ * @return string the generated SQL expression
+ * @throws InvalidParamException if wrong number of operands have been given.
+ */
+ public function buildNotCondition($operator, $operands, &$params)
+ {
+ if (count($operands) != 1) {
+ throw new InvalidParamException("Operator '$operator' requires exactly one operand.");
+ }
+
+ $operand = reset($operands);
+ if (is_array($operand)) {
+ $operand = $this->buildCondition($operand, $params);
+ }
+ if ($operand === '') {
+ return '';
+ }
+ return "$operator ($operand)";
+ }
+
+ /**
+ * Creates an SQL expressions with the `BETWEEN` operator.
+ * @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
+ * @param array $operands the first operand is the column name. The second and third operands
+ * describe the interval that column value should be in.
+ * @param array $params the binding parameters to be populated
+ * @return string the generated SQL expression
+ * @throws InvalidParamException if wrong number of operands have been given.
+ */
+ public function buildBetweenCondition($operator, $operands, &$params)
{
if (!isset($operands[0], $operands[1], $operands[2])) {
- throw new Exception("Operator '$operator' requires three operands.");
+ throw new InvalidParamException("Operator '$operator' requires three operands.");
}
list($column, $value1, $value2) = $operands;
@@ -807,14 +923,26 @@ class QueryBuilder extends \yii\base\Object
$column = $this->db->quoteColumnName($column);
}
$phName1 = self::PARAM_PREFIX . count($params);
- $phName2 = self::PARAM_PREFIX . count($params);
$params[$phName1] = $value1;
+ $phName2 = self::PARAM_PREFIX . count($params);
$params[$phName2] = $value2;
return "$column $operator $phName1 AND $phName2";
}
- private function buildInCondition($operator, $operands, &$params)
+ /**
+ * Creates an SQL expressions with the `IN` operator.
+ * @param string $operator the operator to use (e.g. `IN` or `NOT IN`)
+ * @param array $operands the first operand is the column name. If it is an array
+ * a composite IN condition will be generated.
+ * The second operand is an array of values that column value should be among.
+ * If it is an empty array the generated expression will be a `false` value if
+ * operator is `IN` and empty if operator is `NOT IN`.
+ * @param array $params the binding parameters to be populated
+ * @return string the generated SQL expression
+ * @throws Exception if wrong number of operands have been given.
+ */
+ public function buildInCondition($operator, $operands, &$params)
{
if (!isset($operands[0], $operands[1])) {
throw new Exception("Operator '$operator' requires two operands.");
@@ -824,7 +952,7 @@ class QueryBuilder extends \yii\base\Object
$values = (array)$values;
- if (empty($values) || $column === array()) {
+ if (empty($values) || $column === []) {
return $operator === 'IN' ? '0=1' : '';
}
@@ -864,14 +992,9 @@ class QueryBuilder extends \yii\base\Object
protected function buildCompositeInCondition($operator, $columns, $values, &$params)
{
- foreach ($columns as $i => $column) {
- if (strpos($column, '(') === false) {
- $columns[$i] = $this->db->quoteColumnName($column);
- }
- }
- $vss = array();
+ $vss = [];
foreach ($values as $value) {
- $vs = array();
+ $vs = [];
foreach ($columns as $column) {
if (isset($value[$column])) {
$phName = self::PARAM_PREFIX . count($params);
@@ -883,13 +1006,30 @@ class QueryBuilder extends \yii\base\Object
}
$vss[] = '(' . implode(', ', $vs) . ')';
}
+ foreach ($columns as $i => $column) {
+ if (strpos($column, '(') === false) {
+ $columns[$i] = $this->db->quoteColumnName($column);
+ }
+ }
return '(' . implode(', ', $columns) . ") $operator (" . implode(', ', $vss) . ')';
}
- private function buildLikeCondition($operator, $operands, &$params)
+ /**
+ * Creates an SQL expressions with the `LIKE` operator.
+ * @param string $operator the operator to use (e.g. `LIKE`, `NOT LIKE`, `OR LIKE` or `OR NOT LIKE`)
+ * @param array $operands the first operand is the column name.
+ * The second operand is a single value or an array of values that column value
+ * should be compared with.
+ * If it is an empty array the generated expression will be a `false` value if
+ * operator is `LIKE` or `OR LIKE` and empty if operator is `NOT LIKE` or `OR NOT LIKE`.
+ * @param array $params the binding parameters to be populated
+ * @return string the generated SQL expression
+ * @throws InvalidParamException if wrong number of operands have been given.
+ */
+ public function buildLikeCondition($operator, $operands, &$params)
{
if (!isset($operands[0], $operands[1])) {
- throw new Exception("Operator '$operator' requires two operands.");
+ throw new InvalidParamException("Operator '$operator' requires two operands.");
}
list($column, $values) = $operands;
@@ -911,7 +1051,7 @@ class QueryBuilder extends \yii\base\Object
$column = $this->db->quoteColumnName($column);
}
- $parts = array();
+ $parts = [];
foreach ($values as $value) {
$phName = self::PARAM_PREFIX . count($params);
$params[$phName] = $value;
diff --git a/framework/yii/db/QueryInterface.php b/framework/yii/db/QueryInterface.php
new file mode 100644
index 0000000..f3cc312
--- /dev/null
+++ b/framework/yii/db/QueryInterface.php
@@ -0,0 +1,206 @@
+
+ * @author Carsten Brandt
+ * @since 2.0
+ */
+interface QueryInterface
+{
+ /**
+ * Executes the query and returns all results as an array.
+ * @param Connection $db the database connection used to execute the query.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return array the query results. If the query results in nothing, an empty array will be returned.
+ */
+ public function all($db = null);
+
+ /**
+ * Executes the query and returns a single row of result.
+ * @param Connection $db the database connection used to execute the query.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query
+ * results in nothing.
+ */
+ public function one($db = null);
+
+ /**
+ * Returns the number of records.
+ * @param string $q the COUNT expression. Defaults to '*'.
+ * @param Connection $db the database connection used to execute the query.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return integer number of records
+ */
+ public function count($q = '*', $db = null);
+
+ /**
+ * Returns a value indicating whether the query result contains any row of data.
+ * @param Connection $db the database connection used to execute the query.
+ * If this parameter is not given, the `db` application component will be used.
+ * @return boolean whether the query result contains any row of data.
+ */
+ public function exists($db = null);
+
+ /**
+ * Sets the [[indexBy]] property.
+ * @param string|callable $column the name of the column by which the query results should be indexed by.
+ * This can also be a callable (e.g. anonymous function) that returns the index value based on the given
+ * row data. The signature of the callable should be:
+ *
+ * ~~~
+ * function ($row)
+ * {
+ * // return the index value corresponding to $row
+ * }
+ * ~~~
+ *
+ * @return static the query object itself
+ */
+ public function indexBy($column);
+
+ /**
+ * Sets the WHERE part of the query.
+ *
+ * The method requires a $condition parameter.
+ *
+ * The $condition parameter should be an array in one of the following two formats:
+ *
+ * - hash format: `['column1' => value1, 'column2' => value2, ...]`
+ * - operator format: `[operator, operand1, operand2, ...]`
+ *
+ * A condition in hash format represents the following SQL expression in general:
+ * `column1=value1 AND column2=value2 AND ...`. In case when a value is an array,
+ * an `IN` expression will be generated. And if a value is null, `IS NULL` will be used
+ * in the generated expression. Below are some examples:
+ *
+ * - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`.
+ * - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`.
+ * - `['status' => null] generates `status IS NULL`.
+ *
+ * A condition in operator format generates the SQL expression according to the specified operator, which
+ * can be one of the followings:
+ *
+ * - `and`: the operands should be concatenated together using `AND`. For example,
+ * `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array,
+ * it will be converted into a string using the rules described here. For example,
+ * `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`.
+ * The method will NOT do any quoting or escaping.
+ *
+ * - `or`: similar to the `and` operator except that the operands are concatenated using `OR`.
+ *
+ * - `between`: operand 1 should be the column name, and operand 2 and 3 should be the
+ * starting and ending values of the range that the column is in.
+ * For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`.
+ *
+ * - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
+ * in the generated condition.
+ *
+ * - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing
+ * the range of the values that the column or DB expression should be in. For example,
+ * `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`.
+ * The method will properly quote the column name and escape values in the range.
+ *
+ * - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
+ *
+ * - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
+ * the values that the column or DB expression should be like.
+ * For example, `['like', 'name', '%tester%']` will generate `name LIKE '%tester%'`.
+ * When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
+ * using `AND`. For example, `['like', 'name', ['%test%', '%sample%']]` will generate
+ * `name LIKE '%test%' AND name LIKE '%sample%'`.
+ * The method will properly quote the column name and escape values in the range.
+ *
+ * - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`
+ * predicates when operand 2 is an array.
+ *
+ * - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`
+ * in the generated condition.
+ *
+ * - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate
+ * the `NOT LIKE` predicates.
+ *
+ * @param array $condition the conditions that should be put in the WHERE part.
+ * @return static the query object itself
+ * @see andWhere()
+ * @see orWhere()
+ */
+ public function where($condition);
+
+ /**
+ * Adds an additional WHERE condition to the existing one.
+ * The new condition and the existing one will be joined using the 'AND' operator.
+ * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
+ * on how to specify this parameter.
+ * @return static the query object itself
+ * @see where()
+ * @see orWhere()
+ */
+ public function andWhere($condition);
+
+ /**
+ * Adds an additional WHERE condition to the existing one.
+ * The new condition and the existing one will be joined using the 'OR' operator.
+ * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
+ * on how to specify this parameter.
+ * @return static the query object itself
+ * @see where()
+ * @see andWhere()
+ */
+ public function orWhere($condition);
+
+ /**
+ * Sets the ORDER BY part of the query.
+ * @param string|array $columns the columns (and the directions) to be ordered by.
+ * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
+ * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
+ * The method will automatically quote the column names unless a column contains some parenthesis
+ * (which means the column contains a DB expression).
+ * @return static the query object itself
+ * @see addOrderBy()
+ */
+ public function orderBy($columns);
+
+ /**
+ * Adds additional ORDER BY columns to the query.
+ * @param string|array $columns the columns (and the directions) to be ordered by.
+ * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
+ * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
+ * The method will automatically quote the column names unless a column contains some parenthesis
+ * (which means the column contains a DB expression).
+ * @return static the query object itself
+ * @see orderBy()
+ */
+ public function addOrderBy($columns);
+
+ /**
+ * Sets the LIMIT part of the query.
+ * @param integer $limit the limit. Use null or negative value to disable limit.
+ * @return static the query object itself
+ */
+ public function limit($limit);
+
+ /**
+ * Sets the OFFSET part of the query.
+ * @param integer $offset the offset. Use null or negative value to disable offset.
+ * @return static the query object itself
+ */
+ public function offset($offset);
+}
\ No newline at end of file
diff --git a/framework/yii/db/QueryTrait.php b/framework/yii/db/QueryTrait.php
new file mode 100644
index 0000000..a963869
--- /dev/null
+++ b/framework/yii/db/QueryTrait.php
@@ -0,0 +1,208 @@
+
+ * @author Carsten Brandt
+ * @since 2.0
+ */
+trait QueryTrait
+{
+ /**
+ * @var string|array query condition. This refers to the WHERE clause in a SQL statement.
+ * For example, `age > 31 AND team = 1`.
+ * @see where()
+ */
+ public $where;
+ /**
+ * @var integer maximum number of records to be returned. If not set or less than 0, it means no limit.
+ */
+ public $limit;
+ /**
+ * @var integer zero-based offset from where the records are to be returned. If not set or
+ * less than 0, it means starting from the beginning.
+ */
+ public $offset;
+ /**
+ * @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
+ * The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
+ * can be either [SORT_ASC](http://php.net/manual/en/array.constants.php#constant.sort-asc)
+ * or [SORT_DESC](http://php.net/manual/en/array.constants.php#constant.sort-desc).
+ * The array may also contain [[Expression]] objects. If that is the case, the expressions
+ * will be converted into strings without any change.
+ */
+ public $orderBy;
+ /**
+ * @var string|callable $column the name of the column by which the query results should be indexed by.
+ * This can also be a callable (e.g. anonymous function) that returns the index value based on the given
+ * row data. For more details, see [[indexBy()]]. This property is only used by [[all()]].
+ */
+ public $indexBy;
+
+ /**
+ * Sets the [[indexBy]] property.
+ * @param string|callable $column the name of the column by which the query results should be indexed by.
+ * This can also be a callable (e.g. anonymous function) that returns the index value based on the given
+ * row data. The signature of the callable should be:
+ *
+ * ~~~
+ * function ($row)
+ * {
+ * // return the index value corresponding to $row
+ * }
+ * ~~~
+ *
+ * @return static the query object itself
+ */
+ public function indexBy($column)
+ {
+ $this->indexBy = $column;
+ return $this;
+ }
+
+ /**
+ * Sets the WHERE part of the query.
+ *
+ * See [[QueryInterface::where()]] for detailed documentation.
+ *
+ * @param array $condition the conditions that should be put in the WHERE part.
+ * @return static the query object itself
+ * @see andWhere()
+ * @see orWhere()
+ */
+ public function where($condition)
+ {
+ $this->where = $condition;
+ return $this;
+ }
+
+ /**
+ * Adds an additional WHERE condition to the existing one.
+ * The new condition and the existing one will be joined using the 'AND' operator.
+ * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
+ * on how to specify this parameter.
+ * @return static the query object itself
+ * @see where()
+ * @see orWhere()
+ */
+ public function andWhere($condition)
+ {
+ if ($this->where === null) {
+ $this->where = $condition;
+ } else {
+ $this->where = ['and', $this->where, $condition];
+ }
+ return $this;
+ }
+
+ /**
+ * Adds an additional WHERE condition to the existing one.
+ * The new condition and the existing one will be joined using the 'OR' operator.
+ * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
+ * on how to specify this parameter.
+ * @return static the query object itself
+ * @see where()
+ * @see andWhere()
+ */
+ public function orWhere($condition)
+ {
+ if ($this->where === null) {
+ $this->where = $condition;
+ } else {
+ $this->where = ['or', $this->where, $condition];
+ }
+ return $this;
+ }
+
+ /**
+ * Sets the ORDER BY part of the query.
+ * @param string|array $columns the columns (and the directions) to be ordered by.
+ * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
+ * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
+ * The method will automatically quote the column names unless a column contains some parenthesis
+ * (which means the column contains a DB expression).
+ * @return static the query object itself
+ * @see addOrderBy()
+ */
+ public function orderBy($columns)
+ {
+ $this->orderBy = $this->normalizeOrderBy($columns);
+ return $this;
+ }
+
+ /**
+ * Adds additional ORDER BY columns to the query.
+ * @param string|array $columns the columns (and the directions) to be ordered by.
+ * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
+ * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
+ * The method will automatically quote the column names unless a column contains some parenthesis
+ * (which means the column contains a DB expression).
+ * @return static the query object itself
+ * @see orderBy()
+ */
+ public function addOrderBy($columns)
+ {
+ $columns = $this->normalizeOrderBy($columns);
+ if ($this->orderBy === null) {
+ $this->orderBy = $columns;
+ } else {
+ $this->orderBy = array_merge($this->orderBy, $columns);
+ }
+ return $this;
+ }
+
+ protected function normalizeOrderBy($columns)
+ {
+ if (is_array($columns)) {
+ return $columns;
+ } else {
+ $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
+ $result = [];
+ foreach ($columns as $column) {
+ if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
+ $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? SORT_ASC : SORT_DESC;
+ } else {
+ $result[$column] = SORT_ASC;
+ }
+ }
+ return $result;
+ }
+ }
+
+ /**
+ * Sets the LIMIT part of the query.
+ * @param integer $limit the limit. Use null or negative value to disable limit.
+ * @return static the query object itself
+ */
+ public function limit($limit)
+ {
+ $this->limit = $limit;
+ return $this;
+ }
+
+ /**
+ * Sets the OFFSET part of the query.
+ * @param integer $offset the offset. Use null or negative value to disable offset.
+ * @return static the query object itself
+ */
+ public function offset($offset)
+ {
+ $this->offset = $offset;
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php
index c961244..9698179 100644
--- a/framework/yii/db/Schema.php
+++ b/framework/yii/db/Schema.php
@@ -8,28 +8,34 @@
namespace yii\db;
use Yii;
+use yii\base\Object;
use yii\base\NotSupportedException;
use yii\base\InvalidCallException;
use yii\caching\Cache;
+use yii\caching\GroupDependency;
/**
* Schema is the base class for concrete DBMS-specific schema classes.
*
* Schema represents the database schema information that is DBMS specific.
*
- * @property QueryBuilder $queryBuilder the query builder for the DBMS represented by this schema
- * @property array $tableNames the names of all tables in this database.
- * @property array $tableSchemas the schema information for all tables in this database.
+ * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the
+ * sequence object. This property is read-only.
+ * @property QueryBuilder $queryBuilder The query builder for this connection. This property is read-only.
+ * @property string[] $tableNames All table names in the database. This property is read-only.
+ * @property TableSchema[] $tableSchemas The metadata for all tables in the database. Each array element is an
+ * instance of [[TableSchema]] or its child class. This property is read-only.
*
* @author Qiang Xue
* @since 2.0
*/
-abstract class Schema extends \yii\base\Object
+abstract class Schema extends Object
{
/**
* The followings are the supported abstract column data types.
*/
const TYPE_PK = 'pk';
+ const TYPE_BIGPK = 'bigpk';
const TYPE_STRING = 'string';
const TYPE_TEXT = 'text';
const TYPE_SMALLINT = 'smallint';
@@ -52,11 +58,11 @@ abstract class Schema extends \yii\base\Object
/**
* @var array list of ALL table names in the database
*/
- private $_tableNames = array();
+ private $_tableNames = [];
/**
* @var array list of loaded table metadata (table name => TableSchema)
*/
- private $_tables = array();
+ private $_tables = [];
/**
* @var QueryBuilder the query builder for this database
*/
@@ -86,14 +92,16 @@ abstract class Schema extends \yii\base\Object
$realName = $this->getRawTableName($name);
if ($db->enableSchemaCache && !in_array($name, $db->schemaCacheExclude, true)) {
- /** @var $cache Cache */
+ /** @var Cache $cache */
$cache = is_string($db->schemaCache) ? Yii::$app->getComponent($db->schemaCache) : $db->schemaCache;
if ($cache instanceof Cache) {
$key = $this->getCacheKey($name);
if ($refresh || ($table = $cache->get($key)) === false) {
$table = $this->loadTableSchema($realName);
if ($table !== null) {
- $cache->set($key, $table, $db->schemaCacheDuration);
+ $cache->set($key, $table, $db->schemaCacheDuration, new GroupDependency([
+ 'group' => $this->getCacheGroup(),
+ ]));
}
}
return $this->_tables[$name] = $table;
@@ -107,14 +115,28 @@ abstract class Schema extends \yii\base\Object
* @param string $name the table name
* @return mixed the cache key
*/
- public function getCacheKey($name)
+ protected function getCacheKey($name)
{
- return array(
+ return [
__CLASS__,
$this->db->dsn,
$this->db->username,
$name,
- );
+ ];
+ }
+
+ /**
+ * Returns the cache group name.
+ * This allows [[refresh()]] to invalidate all cached table schemas.
+ * @return string the cache group name
+ */
+ protected function getCacheGroup()
+ {
+ return md5(serialize([
+ __CLASS__,
+ $this->db->dsn,
+ $this->db->username,
+ ]));
}
/**
@@ -122,12 +144,12 @@ abstract class Schema extends \yii\base\Object
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
* @param boolean $refresh whether to fetch the latest available table schemas. If this is false,
* cached data may be returned if available.
- * @return array the metadata for all tables in the database.
+ * @return TableSchema[] the metadata for all tables in the database.
* Each array element is an instance of [[TableSchema]] or its child class.
*/
public function getTableSchemas($schema = '', $refresh = false)
{
- $tables = array();
+ $tables = [];
foreach ($this->getTableNames($schema, $refresh) as $name) {
if ($schema !== '') {
$name = $schema . '.' . $name;
@@ -145,7 +167,7 @@ abstract class Schema extends \yii\base\Object
* If not empty, the returned table names will be prefixed with the schema name.
* @param boolean $refresh whether to fetch the latest available table names. If this is false,
* table names fetched previously (if available) will be returned.
- * @return array all table names in the database.
+ * @return string[] all table names in the database.
*/
public function getTableNames($schema = '', $refresh = false)
{
@@ -167,21 +189,39 @@ abstract class Schema extends \yii\base\Object
}
/**
+ * Determines the PDO type for the given PHP data value.
+ * @param mixed $data the data whose PDO type is to be determined
+ * @return integer the PDO type
+ * @see http://www.php.net/manual/en/pdo.constants.php
+ */
+ public function getPdoType($data)
+ {
+ static $typeMap = [
+ // php type => PDO type
+ 'boolean' => \PDO::PARAM_BOOL,
+ 'integer' => \PDO::PARAM_INT,
+ 'string' => \PDO::PARAM_STR,
+ 'resource' => \PDO::PARAM_LOB,
+ 'NULL' => \PDO::PARAM_NULL,
+ ];
+ $type = gettype($data);
+ return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
+ }
+
+ /**
* Refreshes the schema.
* This method cleans up all cached table schemas so that they can be re-created later
* to reflect the database schema change.
*/
public function refresh()
{
- /** @var $cache Cache */
+ /** @var Cache $cache */
$cache = is_string($this->db->schemaCache) ? Yii::$app->getComponent($this->db->schemaCache) : $this->db->schemaCache;
if ($this->db->enableSchemaCache && $cache instanceof Cache) {
- foreach ($this->_tables as $name => $table) {
- $cache->delete($this->getCacheKey($name));
- }
+ GroupDependency::invalidate($cache, $this->getCacheGroup());
}
- $this->_tableNames = array();
- $this->_tables = array();
+ $this->_tableNames = [];
+ $this->_tables = [];
}
/**
@@ -199,7 +239,7 @@ abstract class Schema extends \yii\base\Object
* This method should be overridden by child classes in order to support this feature
* because the default implementation simply throws an exception.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
- * @return array all table names in the database. The names have NO the schema name prefix.
+ * @return array all table names in the database. The names have NO schema name prefix.
* @throws NotSupportedException if this method is called
*/
protected function findTableNames($schema = '')
@@ -208,6 +248,28 @@ abstract class Schema extends \yii\base\Object
}
/**
+ * Returns all unique indexes for the given table.
+ * Each array element is of the following structure:
+ *
+ * ~~~
+ * [
+ * 'IndexName1' => ['col1' [, ...]],
+ * 'IndexName2' => ['col2' [, ...]],
+ * ]
+ * ~~~
+ *
+ * This method should be overridden by child classes in order to support this feature
+ * because the default implementation simply throws an exception
+ * @param TableSchema $table the table metadata
+ * @return array all unique indexes for the given table.
+ * @throws NotSupportedException if this method is called
+ */
+ public function findUniqueIndexes($table)
+ {
+ throw new NotSupportedException(get_class($this) . ' does not support getting unique indexes information.');
+ }
+
+ /**
* Returns the ID of the last inserted row or sequence value.
* @param string $sequenceName name of the sequence object (required by some DBMS)
* @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
@@ -251,7 +313,7 @@ abstract class Schema extends \yii\base\Object
* then this method will do nothing.
* @param string $name table name
* @return string the properly quoted table name
- * @see quoteSimpleTableName
+ * @see quoteSimpleTableName()
*/
public function quoteTableName($name)
{
@@ -276,7 +338,7 @@ abstract class Schema extends \yii\base\Object
* then this method will do nothing.
* @param string $name column name
* @return string the properly quoted column name
- * @see quoteSimpleColumnName
+ * @see quoteSimpleColumnName()
*/
public function quoteColumnName($name)
{
@@ -340,13 +402,13 @@ abstract class Schema extends \yii\base\Object
*/
protected function getColumnPhpType($column)
{
- static $typeMap = array( // abstract type => php type
+ static $typeMap = [ // abstract type => php type
'smallint' => 'integer',
'integer' => 'integer',
'bigint' => 'integer',
'boolean' => 'boolean',
'float' => 'double',
- );
+ ];
if (isset($typeMap[$column->type])) {
if ($column->type === 'bigint') {
return PHP_INT_SIZE == 8 && !$column->unsigned ? 'integer' : 'string';
diff --git a/framework/yii/db/TableSchema.php b/framework/yii/db/TableSchema.php
index 1065b51..91ce78d 100644
--- a/framework/yii/db/TableSchema.php
+++ b/framework/yii/db/TableSchema.php
@@ -7,25 +7,20 @@
namespace yii\db;
+use yii\base\Object;
use yii\base\InvalidParamException;
/**
* TableSchema represents the metadata of a database table.
*
- * @property array $columnNames list of column names
+ * @property array $columnNames List of column names. This property is read-only.
*
* @author Qiang Xue
* @since 2.0
*/
-class TableSchema extends \yii\base\Object
+class TableSchema extends Object
{
/**
- * @var string name of the catalog (database) that this table belongs to.
- * Defaults to null, meaning no catalog (or the current database).
- * This property is only meaningful for MSSQL.
- */
- public $catalogName;
- /**
* @var string name of the schema that this table belongs to.
*/
public $schemaName;
@@ -36,7 +31,7 @@ class TableSchema extends \yii\base\Object
/**
* @var string[] primary keys of this table.
*/
- public $primaryKey = array();
+ public $primaryKey = [];
/**
* @var string sequence name for the primary key. Null if no sequence.
*/
@@ -45,18 +40,18 @@ class TableSchema extends \yii\base\Object
* @var array foreign keys of this table. Each array element is of the following structure:
*
* ~~~
- * array(
+ * [
* 'ForeignTableName',
* 'fk1' => 'pk1', // pk1 is in foreign table
* 'fk2' => 'pk2', // if composite foreign key
- * )
+ * ]
* ~~~
*/
- public $foreignKeys = array();
+ public $foreignKeys = [];
/**
* @var ColumnSchema[] column metadata of this table. Each array element is a [[ColumnSchema]] object, indexed by column names.
*/
- public $columns = array();
+ public $columns = [];
/**
* Gets the named column metadata.
@@ -86,7 +81,7 @@ class TableSchema extends \yii\base\Object
public function fixPrimaryKey($keys)
{
if (!is_array($keys)) {
- $keys = array($keys);
+ $keys = [$keys];
}
$this->primaryKey = $keys;
foreach ($this->columns as $column) {
diff --git a/framework/yii/db/Transaction.php b/framework/yii/db/Transaction.php
index 195a8c8..e0b90d9 100644
--- a/framework/yii/db/Transaction.php
+++ b/framework/yii/db/Transaction.php
@@ -29,7 +29,8 @@ use yii\base\InvalidConfigException;
* }
* ~~~
*
- * @property boolean $isActive Whether the transaction is active. This property is read-only.
+ * @property boolean $isActive Whether this transaction is active. Only an active transaction can [[commit()]]
+ * or [[rollback()]]. This property is read-only.
*
* @author Qiang Xue
* @since 2.0
diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/yii/db/cubrid/QueryBuilder.php
new file mode 100644
index 0000000..9acf91f
--- /dev/null
+++ b/framework/yii/db/cubrid/QueryBuilder.php
@@ -0,0 +1,90 @@
+
+ * @since 2.0
+ */
+class QueryBuilder extends \yii\db\QueryBuilder
+{
+ /**
+ * @var array mapping from abstract column types (keys) to physical column types (values).
+ */
+ public $typeMap = [
+ Schema::TYPE_PK => 'int NOT NULL AUTO_INCREMENT PRIMARY KEY',
+ Schema::TYPE_BIGPK => 'bigint NOT NULL AUTO_INCREMENT PRIMARY KEY',
+ Schema::TYPE_STRING => 'varchar(255)',
+ Schema::TYPE_TEXT => 'varchar',
+ Schema::TYPE_SMALLINT => 'smallint',
+ Schema::TYPE_INTEGER => 'int',
+ Schema::TYPE_BIGINT => 'bigint',
+ Schema::TYPE_FLOAT => 'float(7)',
+ Schema::TYPE_DECIMAL => 'decimal(10,0)',
+ Schema::TYPE_DATETIME => 'datetime',
+ Schema::TYPE_TIMESTAMP => 'timestamp',
+ Schema::TYPE_TIME => 'time',
+ Schema::TYPE_DATE => 'date',
+ Schema::TYPE_BINARY => 'blob',
+ Schema::TYPE_BOOLEAN => 'smallint',
+ Schema::TYPE_MONEY => 'decimal(19,4)',
+ ];
+
+ /**
+ * Creates a SQL statement for resetting the sequence value of a table's primary key.
+ * The sequence will be reset such that the primary key of the next new row inserted
+ * will have the specified value or 1.
+ * @param string $tableName the name of the table whose primary key sequence will be reset
+ * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
+ * the next new row's primary key will have a value 1.
+ * @return string the SQL statement for resetting sequence
+ * @throws InvalidParamException if the table does not exist or there is no sequence associated with the table.
+ */
+ public function resetSequence($tableName, $value = null)
+ {
+ $table = $this->db->getTableSchema($tableName);
+ if ($table !== null && $table->sequenceName !== null) {
+ $tableName = $this->db->quoteTableName($tableName);
+ if ($value === null) {
+ $key = reset($table->primaryKey);
+ $value = (int)$this->db->createCommand("SELECT MAX(`$key`) FROM " . $this->db->schema->quoteTableName($tableName))->queryScalar() + 1;
+ } else {
+ $value = (int)$value;
+ }
+ return "ALTER TABLE " . $this->db->schema->quoteTableName($tableName) . " AUTO_INCREMENT=$value;";
+ } elseif ($table === null) {
+ throw new InvalidParamException("Table not found: $tableName");
+ } else {
+ throw new InvalidParamException("There is not sequence associated with table '$tableName'.");
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function buildLimit($limit, $offset)
+ {
+ $sql = '';
+ // limit is not optional in CUBRID
+ // http://www.cubrid.org/manual/90/en/LIMIT%20Clause
+ // "You can specify a very big integer for row_count to display to the last row, starting from a specific row."
+ if ($limit !== null && $limit >= 0) {
+ $sql = 'LIMIT ' . (int)$limit;
+ if ($offset > 0) {
+ $sql .= ' OFFSET ' . (int)$offset;
+ }
+ } elseif ($offset > 0) {
+ $sql = 'LIMIT 9223372036854775807 OFFSET ' . (int)$offset; // 2^63-1
+ }
+ return $sql;
+ }
+}
diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php
new file mode 100644
index 0000000..458f2e3
--- /dev/null
+++ b/framework/yii/db/cubrid/Schema.php
@@ -0,0 +1,265 @@
+
+ * @since 2.0
+ */
+class Schema extends \yii\db\Schema
+{
+ /**
+ * @var array mapping from physical column types (keys) to abstract column types (values)
+ * Please refer to [CUBRID manual](http://www.cubrid.org/manual/91/en/sql/datatype.html) for
+ * details on data types.
+ */
+ public $typeMap = [
+ // Numeric data types
+ 'short' => self::TYPE_SMALLINT,
+ 'smallint' => self::TYPE_SMALLINT,
+ 'int' => self::TYPE_INTEGER,
+ 'integer' => self::TYPE_INTEGER,
+ 'bigint' => self::TYPE_BIGINT,
+ 'numeric' => self::TYPE_DECIMAL,
+ 'decimal' => self::TYPE_DECIMAL,
+ 'float' => self::TYPE_FLOAT,
+ 'real' => self::TYPE_FLOAT,
+ 'double' => self::TYPE_FLOAT,
+ 'double precision' => self::TYPE_FLOAT,
+ 'monetary' => self::TYPE_MONEY,
+ // Date/Time data types
+ 'date' => self::TYPE_DATE,
+ 'time' => self::TYPE_TIME,
+ 'timestamp' => self::TYPE_TIMESTAMP,
+ 'datetime' => self::TYPE_DATETIME,
+ // String data types
+ 'char' => self::TYPE_STRING,
+ 'varchar' => self::TYPE_STRING,
+ 'char varying' => self::TYPE_STRING,
+ 'nchar' => self::TYPE_STRING,
+ 'nchar varying' => self::TYPE_STRING,
+ 'string' => self::TYPE_STRING,
+ // BLOB/CLOB data types
+ 'blob' => self::TYPE_BINARY,
+ 'clob' => self::TYPE_BINARY,
+ // Bit string data types
+ 'bit' => self::TYPE_STRING,
+ 'bit varying' => self::TYPE_STRING,
+ // Collection data types (considered strings for now)
+ 'set' => self::TYPE_STRING,
+ 'multiset' => self::TYPE_STRING,
+ 'list' => self::TYPE_STRING,
+ 'sequence' => self::TYPE_STRING,
+ 'enum' => self::TYPE_STRING,
+ ];
+
+ /**
+ * Quotes a table name for use in a query.
+ * A simple table name has no schema prefix.
+ * @param string $name table name
+ * @return string the properly quoted table name
+ */
+ public function quoteSimpleTableName($name)
+ {
+ return strpos($name, '"') !== false ? $name : '"' . $name . '"';
+ }
+
+ /**
+ * Quotes a column name for use in a query.
+ * A simple column name has no prefix.
+ * @param string $name column name
+ * @return string the properly quoted column name
+ */
+ public function quoteSimpleColumnName($name)
+ {
+ return strpos($name, '"') !== false || $name === '*' ? $name : '"' . $name . '"';
+ }
+
+ /**
+ * Quotes a string value for use in a query.
+ * Note that if the parameter is not a string, it will be returned without change.
+ * @param string $str string to be quoted
+ * @return string the properly quoted string
+ * @see http://www.php.net/manual/en/function.PDO-quote.php
+ */
+ public function quoteValue($str)
+ {
+ if (!is_string($str)) {
+ return $str;
+ }
+
+ $this->db->open();
+ // workaround for broken PDO::quote() implementation in CUBRID 9.1.0 http://jira.cubrid.org/browse/APIS-658
+ $version = $this->db->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION);
+ if (version_compare($version, '8.4.4.0002', '<') || $version[0] == '9' && version_compare($version, '9.2.0.0002', '<=')) {
+ return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'";
+ } else {
+ return $this->db->pdo->quote($str);
+ }
+ }
+
+ /**
+ * Creates a query builder for the CUBRID database.
+ * @return QueryBuilder query builder instance
+ */
+ public function createQueryBuilder()
+ {
+ return new QueryBuilder($this->db);
+ }
+
+ /**
+ * Loads the metadata for the specified table.
+ * @param string $name table name
+ * @return TableSchema driver dependent table metadata. Null if the table does not exist.
+ */
+ protected function loadTableSchema($name)
+ {
+ $this->db->open();
+ $tableInfo = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE, $name);
+
+ if (isset($tableInfo[0]['NAME'])) {
+ $table = new TableSchema();
+ $table->name = $tableInfo[0]['NAME'];
+
+ $sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteSimpleTableName($table->name);
+ $columns = $this->db->createCommand($sql)->queryAll();
+
+ foreach ($columns as $info) {
+ $column = $this->loadColumnSchema($info);
+ $table->columns[$column->name] = $column;
+ }
+
+ $primaryKeys = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_PRIMARY_KEY, $table->name);
+ foreach ($primaryKeys as $key) {
+ $column = $table->columns[$key['ATTR_NAME']];
+ $column->isPrimaryKey = true;
+ $table->primaryKey[] = $column->name;
+ if ($column->autoIncrement) {
+ $table->sequenceName = '';
+ }
+ }
+
+ $foreignKeys = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $table->name);
+ foreach ($foreignKeys as $key) {
+ if (isset($table->foreignKeys[$key['FK_NAME']])) {
+ $table->foreignKeys[$key['FK_NAME']][$key['FKCOLUMN_NAME']] = $key['PKCOLUMN_NAME'];
+ } else {
+ $table->foreignKeys[$key['FK_NAME']] = [
+ $key['PKTABLE_NAME'],
+ $key['FKCOLUMN_NAME'] => $key['PKCOLUMN_NAME']
+ ];
+ }
+ }
+ $table->foreignKeys = array_values($table->foreignKeys);
+
+ return $table;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Loads the column information into a [[ColumnSchema]] object.
+ * @param array $info column information
+ * @return ColumnSchema the column schema object
+ */
+ protected function loadColumnSchema($info)
+ {
+ $column = new ColumnSchema();
+
+ $column->name = $info['Field'];
+ $column->allowNull = $info['Null'] === 'YES';
+ $column->isPrimaryKey = false; // primary key will be set by loadTableSchema() later
+ $column->autoIncrement = stripos($info['Extra'], 'auto_increment') !== false;
+
+ $column->dbType = strtolower($info['Type']);
+ $column->unsigned = strpos($column->dbType, 'unsigned') !== false;
+
+ $column->type = self::TYPE_STRING;
+ if (preg_match('/^([\w ]+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) {
+ $type = $matches[1];
+ if (isset($this->typeMap[$type])) {
+ $column->type = $this->typeMap[$type];
+ }
+ if (!empty($matches[2])) {
+ if ($type === 'enum') {
+ $values = explode(',', $matches[2]);
+ foreach ($values as $i => $value) {
+ $values[$i] = trim($value, "'");
+ }
+ $column->enumValues = $values;
+ } else {
+ $values = explode(',', $matches[2]);
+ $column->size = $column->precision = (int)$values[0];
+ if (isset($values[1])) {
+ $column->scale = (int)$values[1];
+ }
+ }
+ }
+ }
+
+ $column->phpType = $this->getColumnPhpType($column);
+
+ if ($column->type === 'timestamp' && $info['Default'] === 'CURRENT_TIMESTAMP' ||
+ $column->type === 'datetime' && $info['Default'] === 'SYS_DATETIME' ||
+ $column->type === 'date' && $info['Default'] === 'SYS_DATE' ||
+ $column->type === 'time' && $info['Default'] === 'SYS_TIME'
+ ) {
+ $column->defaultValue = new Expression($info['Default']);
+ } else {
+ $column->defaultValue = $column->typecast($info['Default']);
+ }
+
+ return $column;
+ }
+
+ /**
+ * Returns all table names in the database.
+ * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
+ * @return array all table names in the database. The names have NO schema name prefix.
+ */
+ protected function findTableNames($schema = '')
+ {
+ $this->db->open();
+ $tables = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE);
+ $tableNames = [];
+ foreach ($tables as $table) {
+ // do not list system tables
+ if ($table['TYPE'] != 0) {
+ $tableNames[] = $table['NAME'];
+ }
+ }
+ return $tableNames;
+ }
+
+ /**
+ * Determines the PDO type for the given PHP data value.
+ * @param mixed $data the data whose PDO type is to be determined
+ * @return integer the PDO type
+ * @see http://www.php.net/manual/en/pdo.constants.php
+ */
+ public function getPdoType($data)
+ {
+ static $typeMap = [
+ // php type => PDO type
+ 'boolean' => \PDO::PARAM_INT, // PARAM_BOOL is not supported by CUBRID PDO
+ 'integer' => \PDO::PARAM_INT,
+ 'string' => \PDO::PARAM_STR,
+ 'resource' => \PDO::PARAM_LOB,
+ 'NULL' => \PDO::PARAM_NULL,
+ ];
+ $type = gettype($data);
+ return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
+ }
+}
diff --git a/framework/yii/db/mssql/QueryBuilder.php b/framework/yii/db/mssql/QueryBuilder.php
index e7f8f80..77b9532 100644
--- a/framework/yii/db/mssql/QueryBuilder.php
+++ b/framework/yii/db/mssql/QueryBuilder.php
@@ -20,23 +20,24 @@ class QueryBuilder extends \yii\db\QueryBuilder
/**
* @var array mapping from abstract column types (keys) to physical column types (values).
*/
- public $typeMap = array(
+ public $typeMap = [
Schema::TYPE_PK => 'int IDENTITY PRIMARY KEY',
+ Schema::TYPE_BIGPK => 'bigint IDENTITY PRIMARY KEY',
Schema::TYPE_STRING => 'varchar(255)',
Schema::TYPE_TEXT => 'text',
- Schema::TYPE_SMALLINT => 'smallint(6)',
- Schema::TYPE_INTEGER => 'int(11)',
- Schema::TYPE_BIGINT => 'bigint(20)',
+ Schema::TYPE_SMALLINT => 'smallint',
+ Schema::TYPE_INTEGER => 'int',
+ Schema::TYPE_BIGINT => 'bigint',
Schema::TYPE_FLOAT => 'float',
- Schema::TYPE_DECIMAL => 'decimal(10,0)',
+ Schema::TYPE_DECIMAL => 'decimal',
Schema::TYPE_DATETIME => 'datetime',
Schema::TYPE_TIMESTAMP => 'timestamp',
Schema::TYPE_TIME => 'time',
Schema::TYPE_DATE => 'date',
Schema::TYPE_BINARY => 'binary',
- Schema::TYPE_BOOLEAN => 'tinyint(1)',
+ Schema::TYPE_BOOLEAN => 'bit',
Schema::TYPE_MONEY => 'decimal(19,4)',
- );
+ ];
// public function update($table, $columns, $condition, &$params)
// {
@@ -59,6 +60,47 @@ class QueryBuilder extends \yii\db\QueryBuilder
// }
/**
+ * Builds a SQL statement for renaming a DB table.
+ * @param string $table the table to be renamed. The name will be properly quoted by the method.
+ * @param string $newName the new table name. The name will be properly quoted by the method.
+ * @return string the SQL statement for renaming a DB table.
+ */
+ public function renameTable($table, $newName)
+ {
+ return "sp_rename '$table', '$newName'";
+ }
+
+ /**
+ * Builds a SQL statement for renaming a column.
+ * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
+ * @param string $name the old name of the column. The name will be properly quoted by the method.
+ * @param string $newName the new name of the column. The name will be properly quoted by the method.
+ * @return string the SQL statement for renaming a DB column.
+ */
+ public function renameColumn($table, $name, $newName)
+ {
+ return "sp_rename '$table.$name', '$newName', 'COLUMN'";
+ }
+
+ /**
+ * Builds a SQL statement for changing the definition of a column.
+ * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
+ * @param string $column the name of the column to be changed. The name will be properly quoted by the method.
+ * @param string $type the new column type. The {@link getColumnType} method will be invoked to convert abstract column type (if any)
+ * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
+ * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
+ * @return string the SQL statement for changing the definition of a column.
+ */
+ public function alterColumn($table, $column, $type)
+ {
+ $type=$this->getColumnType($type);
+ $sql='ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN '
+ . $this->db->quoteColumnName($column) . ' '
+ . $this->getColumnType($type);
+ return $sql;
+ }
+
+ /**
* Builds a SQL statement for enabling or disabling integrity check.
* @param boolean $check whether to turn on or off the integrity check.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
diff --git a/framework/yii/db/mssql/Schema.php b/framework/yii/db/mssql/Schema.php
index ad0f7d4..a6c4ffa 100644
--- a/framework/yii/db/mssql/Schema.php
+++ b/framework/yii/db/mssql/Schema.php
@@ -7,7 +7,6 @@
namespace yii\db\mssql;
-use yii\db\TableSchema;
use yii\db\ColumnSchema;
/**
@@ -26,7 +25,7 @@ class Schema extends \yii\db\Schema
/**
* @var array mapping from physical column types (keys) to abstract column types (values)
*/
- public $typeMap = array(
+ public $typeMap = [
// exact numerics
'bigint' => self::TYPE_BIGINT,
'numeric' => self::TYPE_DECIMAL,
@@ -73,7 +72,7 @@ class Schema extends \yii\db\Schema
'sql_variant' => self::TYPE_STRING,
'xml' => self::TYPE_STRING,
'table' => self::TYPE_STRING,
- );
+ ];
/**
* Quotes a table name for use in a query.
@@ -119,6 +118,8 @@ class Schema extends \yii\db\Schema
if ($this->findColumns($table)) {
$this->findForeignKeys($table);
return $table;
+ } else {
+ return null;
}
}
@@ -129,7 +130,7 @@ class Schema extends \yii\db\Schema
*/
protected function resolveTableNames($table, $name)
{
- $parts = explode('.', str_replace(array('[', ']'), '', $name));
+ $parts = explode('.', str_replace(['[', ']'], '', $name));
$partCount = count($parts);
if ($partCount == 3) {
// catalog name, schema name and table name passed
@@ -159,7 +160,7 @@ class Schema extends \yii\db\Schema
$column->name = $info['column_name'];
$column->allowNull = $info['is_nullable'] == 'YES';
$column->dbType = $info['data_type'];
- $column->enumValues = array(); // mssql has only vague equivalents to enum
+ $column->enumValues = []; // mssql has only vague equivalents to enum
$column->isPrimaryKey = null; // primary key will be determined in findColumns() method
$column->autoIncrement = $info['is_identity'] == 1;
$column->unsigned = stripos($column->dbType, 'unsigned') !== false;
@@ -284,7 +285,7 @@ WHERE
SQL;
$table->primaryKey = $this->db
- ->createCommand($sql, array(':tableName' => $table->name, ':schemaName' => $table->schemaName))
+ ->createCommand($sql, [':tableName' => $table->name, ':schemaName' => $table->schemaName])
->queryColumn();
}
@@ -323,19 +324,17 @@ JOIN {$keyColumnUsageTableName} AS [kcu2] ON
WHERE [kcu1].[table_name] = :tableName
SQL;
- $rows = $this->db->createCommand($sql, array(':tableName' => $table->name))->queryAll();
- $table->foreignKeys = array();
+ $rows = $this->db->createCommand($sql, [':tableName' => $table->name])->queryAll();
+ $table->foreignKeys = [];
foreach ($rows as $row) {
- $table->foreignKeys[] = array($row['uq_table_name'], $row['fk_column_name'] => $row['uq_column_name']);
+ $table->foreignKeys[] = [$row['uq_table_name'], $row['fk_column_name'] => $row['uq_column_name']];
}
}
/**
* Returns all table names in the database.
- * This method should be overridden by child classes in order to support this feature
- * because the default implementation simply throws an exception.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
- * @return array all table names in the database. The names have NO the schema name prefix.
+ * @return array all table names in the database. The names have NO schema name prefix.
*/
protected function findTableNames($schema = '')
{
@@ -349,12 +348,6 @@ FROM [information_schema].[tables] AS [t]
WHERE [t].[table_schema] = :schema AND [t].[table_type] = 'BASE TABLE'
SQL;
- $names = $this->db->createCommand($sql, array(':schema' => $schema))->queryColumn();
- if ($schema !== static::DEFAULT_SCHEMA) {
- foreach ($names as $index => $name) {
- $names[$index] = $schema . '.' . $name;
- }
- }
- return $names;
+ return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn();
}
}
diff --git a/framework/yii/db/mssql/TableSchema.php b/framework/yii/db/mssql/TableSchema.php
new file mode 100644
index 0000000..67ad85c
--- /dev/null
+++ b/framework/yii/db/mssql/TableSchema.php
@@ -0,0 +1,23 @@
+
+ * @since 2.0
+ */
+class TableSchema extends \yii\db\TableSchema
+{
+ /**
+ * @var string name of the catalog (database) that this table belongs to.
+ * Defaults to null, meaning no catalog (or the current database).
+ */
+ public $catalogName;
+}
diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/yii/db/mysql/QueryBuilder.php
index 4b35e24..e9481a4 100644
--- a/framework/yii/db/mysql/QueryBuilder.php
+++ b/framework/yii/db/mysql/QueryBuilder.php
@@ -21,8 +21,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
/**
* @var array mapping from abstract column types (keys) to physical column types (values).
*/
- public $typeMap = array(
+ public $typeMap = [
Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',
+ Schema::TYPE_BIGPK => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY',
Schema::TYPE_STRING => 'varchar(255)',
Schema::TYPE_TEXT => 'text',
Schema::TYPE_SMALLINT => 'smallint(6)',
@@ -37,7 +38,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_BINARY => 'blob',
Schema::TYPE_BOOLEAN => 'tinyint(1)',
Schema::TYPE_MONEY => 'decimal(19,4)',
- );
+ ];
/**
* Builds a SQL statement for renaming a column.
@@ -50,7 +51,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
public function renameColumn($table, $oldName, $newName)
{
$quotedTable = $this->db->quoteTableName($table);
- $row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryRow();
+ $row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryOne();
if ($row === false) {
throw new Exception("Unable to find column '$oldName' in table '$table'.");
}
@@ -89,6 +90,17 @@ class QueryBuilder extends \yii\db\QueryBuilder
}
/**
+ * Builds a SQL statement for removing a primary key constraint to an existing table.
+ * @param string $name the name of the primary key constraint to be removed.
+ * @param string $table the table that the primary key constraint will be removed from.
+ * @return string the SQL statement for removing a primary key constraint from an existing table.
+ */
+ public function dropPrimaryKey($name, $table)
+ {
+ return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' DROP PRIMARY KEY';
+ }
+
+ /**
* Creates a SQL statement for resetting the sequence value of a table's primary key.
* The sequence will be reset such that the primary key of the next new row inserted
* will have the specified value or 1.
@@ -113,7 +125,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
} elseif ($table === null) {
throw new InvalidParamException("Table not found: $tableName");
} else {
- throw new InvalidParamException("There is not sequence associated with table '$tableName'.'");
+ throw new InvalidParamException("There is not sequence associated with table '$tableName'.");
}
}
@@ -130,40 +142,22 @@ class QueryBuilder extends \yii\db\QueryBuilder
}
/**
- * Generates a batch INSERT SQL statement.
- * For example,
- *
- * ~~~
- * $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
- * array('Tom', 30),
- * array('Jane', 20),
- * array('Linda', 25),
- * ))->execute();
- * ~~~
- *
- * Not that the values in each row must match the corresponding column names.
- *
- * @param string $table the table that new rows will be inserted into.
- * @param array $columns the column names
- * @param array $rows the rows to be batch inserted into the table
- * @return string the batch INSERT SQL statement
+ * @inheritdoc
*/
- public function batchInsert($table, $columns, $rows)
+ public function buildLimit($limit, $offset)
{
- foreach ($columns as $i => $name) {
- $columns[$i] = $this->db->quoteColumnName($name);
- }
-
- $values = array();
- foreach ($rows as $row) {
- $vs = array();
- foreach ($row as $value) {
- $vs[] = is_string($value) ? $this->db->quoteValue($value) : $value;
+ $sql = '';
+ // limit is not optional in MySQL
+ // http://stackoverflow.com/a/271650/1106908
+ // http://dev.mysql.com/doc/refman/5.0/en/select.html#idm47619502796240
+ if ($limit !== null && $limit >= 0) {
+ $sql = 'LIMIT ' . (int)$limit;
+ if ($offset > 0) {
+ $sql .= ' OFFSET ' . (int)$offset;
}
- $values[] = '(' . implode(', ', $vs) . ')';
+ } elseif ($offset > 0) {
+ $sql = 'LIMIT ' . (int)$offset . ', 18446744073709551615'; // 2^64-1
}
-
- return 'INSERT INTO ' . $this->db->quoteTableName($table)
- . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);
+ return $sql;
}
}
diff --git a/framework/yii/db/mysql/Schema.php b/framework/yii/db/mysql/Schema.php
index b42ef15..a649d8a 100644
--- a/framework/yii/db/mysql/Schema.php
+++ b/framework/yii/db/mysql/Schema.php
@@ -21,7 +21,7 @@ class Schema extends \yii\db\Schema
/**
* @var array mapping from physical column types (keys) to abstract column types (values)
*/
- public $typeMap = array(
+ public $typeMap = [
'tinyint' => self::TYPE_SMALLINT,
'bit' => self::TYPE_SMALLINT,
'smallint' => self::TYPE_SMALLINT,
@@ -47,7 +47,7 @@ class Schema extends \yii\db\Schema
'time' => self::TYPE_TIME,
'timestamp' => self::TYPE_TIMESTAMP,
'enum' => self::TYPE_STRING,
- );
+ ];
/**
* Quotes a table name for use in a query.
@@ -207,25 +207,36 @@ class Schema extends \yii\db\Schema
}
/**
- * Collects the foreign key column details for the given table.
+ * Gets the CREATE TABLE sql string.
* @param TableSchema $table the table metadata
+ * @return string $sql the result of 'SHOW CREATE TABLE'
*/
- protected function findConstraints($table)
+ protected function getCreateTableSql($table)
{
- $row = $this->db->createCommand('SHOW CREATE TABLE ' . $this->quoteSimpleTableName($table->name))->queryRow();
+ $row = $this->db->createCommand('SHOW CREATE TABLE ' . $this->quoteSimpleTableName($table->name))->queryOne();
if (isset($row['Create Table'])) {
$sql = $row['Create Table'];
} else {
$row = array_values($row);
$sql = $row[1];
}
+ return $sql;
+ }
+
+ /**
+ * Collects the foreign key column details for the given table.
+ * @param TableSchema $table the table metadata
+ */
+ protected function findConstraints($table)
+ {
+ $sql = $this->getCreateTableSql($table);
$regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi';
if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$fks = array_map('trim', explode(',', str_replace('`', '', $match[1])));
$pks = array_map('trim', explode(',', str_replace('`', '', $match[3])));
- $constraint = array(str_replace('`', '', $match[2]));
+ $constraint = [str_replace('`', '', $match[2])];
foreach ($fks as $k => $name) {
$constraint[$name] = $pks[$k];
}
@@ -235,11 +246,39 @@ class Schema extends \yii\db\Schema
}
/**
+ * Returns all unique indexes for the given table.
+ * Each array element is of the following structure:
+ *
+ * ~~~
+ * [
+ * 'IndexName1' => ['col1' [, ...]],
+ * 'IndexName2' => ['col2' [, ...]],
+ * ]
+ * ~~~
+ *
+ * @param TableSchema $table the table metadata
+ * @return array all unique indexes for the given table.
+ */
+ public function findUniqueIndexes($table)
+ {
+ $sql = $this->getCreateTableSql($table);
+ $uniqueIndexes = [];
+
+ $regexp = '/UNIQUE KEY\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi';
+ if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) {
+ foreach ($matches as $match) {
+ $indexName = str_replace('`', '', $match[1]);
+ $indexColumns = array_map('trim', explode(',', str_replace('`', '', $match[2])));
+ $uniqueIndexes[$indexName] = $indexColumns;
+ }
+ }
+ return $uniqueIndexes;
+ }
+
+ /**
* Returns all table names in the database.
- * This method should be overridden by child classes in order to support this feature
- * because the default implementation simply throws an exception.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
- * @return array all table names in the database. The names have NO the schema name prefix.
+ * @return array all table names in the database. The names have NO schema name prefix.
*/
protected function findTableNames($schema = '')
{
diff --git a/framework/yii/db/oci/QueryBuilder.php b/framework/yii/db/oci/QueryBuilder.php
new file mode 100644
index 0000000..cf8234f
--- /dev/null
+++ b/framework/yii/db/oci/QueryBuilder.php
@@ -0,0 +1,137 @@
+params;
+ $clauses = [
+ $this->buildSelect($query->select, $query->distinct, $query->selectOption),
+ $this->buildFrom($query->from),
+ $this->buildJoin($query->join, $params),
+ $this->buildWhere($query->where, $params),
+ $this->buildGroupBy($query->groupBy),
+ $this->buildHaving($query->having, $params),
+ $this->buildUnion($query->union, $params),
+ $this->buildOrderBy($query->orderBy),
+ ];
+ $this->sql = implode($this->separator, array_filter($clauses));
+
+ if ($query->limit !== null || $query->offset !== null) {
+ $this->sql = $this->buildLimit($query->limit, $query->offset);
+ }
+ return [$this->sql, $params];
+ }
+
+ public function buildLimit($limit, $offset)
+ {
+ if (($limit < 0) && ($offset < 0)) {
+ return $this->sql;
+ }
+ $filters = [];
+ if ($offset > 0) {
+ $filters[] = 'rowNumId > ' . (int)$offset;
+ }
+
+ if ($limit >= 0) {
+ $filters[] = 'rownum <= ' . (int)$limit;
+ }
+
+ if (count($filters) > 0) {
+ $filter = implode(' and ', $filters);
+ $filter = " WHERE " . $filter;
+ } else {
+ $filter = '';
+ }
+
+ $sql = <<sql}),
+ PAGINATION AS (SELECT USER_SQL.*, rownum as rowNumId FROM USER_SQL)
+SELECT *
+FROM PAGINATION
+{$filter}
+EOD;
+ return $sql;
+ }
+
+
+ /**
+ * Builds a SQL statement for renaming a DB table.
+ *
+ * @param string $table the table to be renamed. The name will be properly quoted by the method.
+ * @param string $newName the new table name. The name will be properly quoted by the method.
+ * @return string the SQL statement for renaming a DB table.
+ */
+ public function renameTable($table, $newName)
+ {
+ return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' RENAME TO ' . $this->db->quoteTableName($newName);
+ }
+
+ /**
+ * Builds a SQL statement for changing the definition of a column.
+ *
+ * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
+ * @param string $column the name of the column to be changed. The name will be properly quoted by the method.
+ * @param string $type the new column type. The {@link getColumnType} method will be invoked to convert abstract column type (if any)
+ * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
+ * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
+ * @return string the SQL statement for changing the definition of a column.
+ */
+ public function alterColumn($table, $column, $type)
+ {
+ $type = $this->getColumnType($type);
+ return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' MODIFY ' . $this->db->quoteColumnName($column) . ' ' . $this->getColumnType($type);
+ }
+
+ /**
+ * Builds a SQL statement for dropping an index.
+ *
+ * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
+ * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
+ * @return string the SQL statement for dropping an index.
+ */
+ public function dropIndex($name, $table)
+ {
+ return 'DROP INDEX ' . $this->db->quoteTableName($name);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resetSequence($table, $value = null)
+ {
+ $tableSchema = $this->db->getTableSchema($table);
+ if ($tableSchema === null) {
+ throw new InvalidParamException("Unknown table: $table");
+ }
+ if ($tableSchema->sequenceName === null) {
+ return '';
+ }
+
+ if ($value !== null) {
+ $value = (int)$value;
+ } else {
+ $value = (int)$this->db->createCommand("SELECT MAX(\"{$tableSchema->primaryKey}\") FROM \"{$tableSchema->name}\"")->queryScalar();
+ $value++;
+ }
+ return "DROP SEQUENCE \"{$tableSchema->name}_SEQ\";"
+ . "CREATE SEQUENCE \"{$tableSchema->name}_SEQ\" START WITH {$value} INCREMENT BY 1 NOMAXVALUE NOCACHE";
+ }
+}
diff --git a/framework/yii/db/oci/Schema.php b/framework/yii/db/oci/Schema.php
new file mode 100644
index 0000000..b9a51c2
--- /dev/null
+++ b/framework/yii/db/oci/Schema.php
@@ -0,0 +1,273 @@
+
+ * @since 2.0
+ */
+class Schema extends \yii\db\Schema
+{
+ private $_defaultSchema;
+
+ /**
+ * @inheritdoc
+ */
+ public function quoteSimpleTableName($name)
+ {
+ return '"' . $name . '"';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function quoteSimpleColumnName($name)
+ {
+ return '"' . $name . '"';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function createQueryBuilder()
+ {
+ return new QueryBuilder($this->db);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function loadTableSchema($name)
+ {
+ $table = new TableSchema();
+ $this->resolveTableNames($table, $name);
+
+ if ($this->findColumns($table)) {
+ $this->findConstraints($table);
+ return $table;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Resolves the table name and schema name (if any).
+ *
+ * @param TableSchema $table the table metadata object
+ * @param string $name the table name
+ */
+ protected function resolveTableNames($table, $name)
+ {
+ $parts = explode('.', str_replace('"', '', $name));
+ if (isset($parts[1])) {
+ $table->schemaName = $parts[0];
+ $table->name = $parts[1];
+ } else {
+ $table->schemaName = $this->getDefaultSchema();
+ $table->name = $parts[0];
+ }
+ }
+
+ /**
+ * @return string default schema.
+ */
+ public function getDefaultSchema()
+ {
+ if ($this->_defaultSchema === null) {
+ $this->setDefaultSchema(strtoupper($this->db->username));
+ }
+ return $this->_defaultSchema;
+ }
+
+ /**
+ * @param string $schema default schema.
+ */
+ public function setDefaultSchema($schema)
+ {
+ $this->_defaultSchema = $schema;
+ }
+
+ /**
+ * Collects the table column metadata.
+ * @param TableSchema $table the table schema
+ * @return boolean whether the table exists
+ */
+ protected function findColumns($table)
+ {
+ $schemaName = $table->schemaName;
+ $tableName = $table->name;
+
+ $sql = << 0 then ',' || a.data_scale else '' end
+ || ')'
+ when data_type = 'DATE' then ''
+ when data_type = 'NUMBER' then ''
+ else '(' || to_char(a.data_length) || ')'
+ end as data_type,
+ a.nullable, a.data_default,
+ ( SELECT D.constraint_type
+ FROM ALL_CONS_COLUMNS C
+ inner join ALL_constraints D on D.OWNER = C.OWNER and D.constraint_name = C.constraint_name
+ WHERE C.OWNER = B.OWNER
+ and C.table_name = B.object_name
+ and C.column_name = A.column_name
+ and D.constraint_type = 'P') as Key,
+ com.comments as column_comment
+FROM ALL_TAB_COLUMNS A
+inner join ALL_OBJECTS B ON b.owner = a.owner and ltrim(B.OBJECT_NAME) = ltrim(A.TABLE_NAME)
+LEFT JOIN user_col_comments com ON (A.table_name = com.table_name AND A.column_name = com.column_name)
+WHERE
+ a.owner = '{$schemaName}'
+ and (b.object_type = 'TABLE' or b.object_type = 'VIEW')
+ and b.object_name = '{$tableName}'
+ORDER by a.column_id
+EOD;
+
+ try {
+ $columns = $this->db->createCommand($sql)->queryAll();
+ } catch (\Exception $e) {
+ return false;
+ }
+
+ foreach ($columns as $column) {
+ $c = $this->createColumn($column);
+ $table->columns[$c->name] = $c;
+ if ($c->isPrimaryKey) {
+ $table->primaryKey[] = $c->name;
+ $table->sequenceName = '';
+ $c->autoIncrement = true;
+ }
+ }
+ return true;
+ }
+
+ protected function createColumn($column)
+ {
+ $c = new ColumnSchema();
+ $c->name = $column['COLUMN_NAME'];
+ $c->allowNull = $column['NULLABLE'] === 'Y';
+ $c->isPrimaryKey = strpos($column['KEY'], 'P') !== false;
+ $c->comment = $column['COLUMN_COMMENT'] === null ? '' : $column['COLUMN_COMMENT'];
+
+ $this->extractColumnType($c, $column['DATA_TYPE']);
+ $this->extractColumnSize($c, $column['DATA_TYPE']);
+
+ if (stripos($column['DATA_DEFAULT'], 'timestamp') !== false) {
+ $c->defaultValue = null;
+ } else {
+ $c->defaultValue = $c->typecast($column['DATA_DEFAULT']);
+ }
+
+ return $c;
+ }
+
+ protected function findConstraints($table)
+ {
+ $sql = << 'P'
+ order by d.constraint_name, c.position
+EOD;
+ $command = $this->db->createCommand($sql);
+ foreach ($command->queryAll() as $row) {
+ if ($row['CONSTRAINT_TYPE'] === 'R') {
+ $name = $row["COLUMN_NAME"];
+ $table->foreignKeys[$name] = [$row["TABLE_REF"], $row["COLUMN_REF"]];
+ }
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function findTableNames($schema = '')
+ {
+ if ($schema === '') {
+ $sql = <<db->createCommand($sql);
+ } else {
+ $sql = <<db->createCommand($sql);
+ $command->bindParam(':schema', $schema);
+ }
+
+ $rows = $command->queryAll();
+ $names = [];
+ foreach ($rows as $row) {
+ $names[] = $row['TABLE_NAME'];
+ }
+ return $names;
+ }
+
+ /**
+ * Extracts the data types for the given column
+ * @param ColumnSchema $column
+ * @param string $dbType DB type
+ */
+ protected function extractColumnType($column, $dbType)
+ {
+ $column->dbType = $dbType;
+
+ if (strpos($dbType, 'FLOAT') !== false) {
+ $column->type = 'double';
+ } elseif (strpos($dbType, 'NUMBER') !== false || strpos($dbType, 'INTEGER') !== false) {
+ if (strpos($dbType, '(') && preg_match('/\((.*)\)/', $dbType, $matches)) {
+ $values = explode(',', $matches[1]);
+ if (isset($values[1]) and (((int)$values[1]) > 0)) {
+ $column->type = 'double';
+ } else {
+ $column->type = 'integer';
+ }
+ } else {
+ $column->type = 'double';
+ }
+ } else {
+ $column->type = 'string';
+ }
+ }
+
+ /**
+ * Extracts size, precision and scale information from column's DB type.
+ * @param ColumnSchema $column
+ * @param string $dbType the column's DB type
+ */
+ protected function extractColumnSize($column, $dbType)
+ {
+ if (strpos($dbType, '(') && preg_match('/\((.*)\)/', $dbType, $matches)) {
+ $values = explode(',', $matches[1]);
+ $column->size = $column->precision = (int)$values[0];
+ if (isset($values[1])) {
+ $column->scale = (int)$values[1];
+ }
+ }
+ }
+}
diff --git a/framework/yii/db/pgsql/QueryBuilder.php b/framework/yii/db/pgsql/QueryBuilder.php
index 3417ad9..09a620d 100644
--- a/framework/yii/db/pgsql/QueryBuilder.php
+++ b/framework/yii/db/pgsql/QueryBuilder.php
@@ -20,22 +20,61 @@ class QueryBuilder extends \yii\db\QueryBuilder
/**
* @var array mapping from abstract column types (keys) to physical column types (values).
*/
- public $typeMap = array(
- Schema::TYPE_PK => 'serial not null primary key',
- Schema::TYPE_STRING => 'varchar',
- Schema::TYPE_TEXT => 'text',
- Schema::TYPE_SMALLINT => 'smallint',
- Schema::TYPE_INTEGER => 'integer',
- Schema::TYPE_BIGINT => 'bigint',
- Schema::TYPE_FLOAT => 'double precision',
- Schema::TYPE_DECIMAL => 'numeric',
- Schema::TYPE_DATETIME => 'timestamp',
- Schema::TYPE_TIMESTAMP => 'timestamp',
- Schema::TYPE_TIME => 'time',
- Schema::TYPE_DATE => 'date',
- Schema::TYPE_BINARY => 'bytea',
- Schema::TYPE_BOOLEAN => 'boolean',
- Schema::TYPE_MONEY => 'numeric(19,4)',
- );
+ public $typeMap = [
+ Schema::TYPE_PK => 'serial NOT NULL PRIMARY KEY',
+ Schema::TYPE_BIGPK => 'bigserial NOT NULL PRIMARY KEY',
+ Schema::TYPE_STRING => 'varchar(255)',
+ Schema::TYPE_TEXT => 'text',
+ Schema::TYPE_SMALLINT => 'smallint',
+ Schema::TYPE_INTEGER => 'integer',
+ Schema::TYPE_BIGINT => 'bigint',
+ Schema::TYPE_FLOAT => 'double precision',
+ Schema::TYPE_DECIMAL => 'numeric(10,0)',
+ Schema::TYPE_DATETIME => 'timestamp',
+ Schema::TYPE_TIMESTAMP => 'timestamp',
+ Schema::TYPE_TIME => 'time',
+ Schema::TYPE_DATE => 'date',
+ Schema::TYPE_BINARY => 'bytea',
+ Schema::TYPE_BOOLEAN => 'boolean',
+ Schema::TYPE_MONEY => 'numeric(19,4)',
+ ];
+ /**
+ * Builds a SQL statement for dropping an index.
+ * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
+ * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
+ * @return string the SQL statement for dropping an index.
+ */
+ public function dropIndex($name, $table)
+ {
+ return 'DROP INDEX ' . $this->db->quoteTableName($name);
+ }
+
+ /**
+ * Builds a SQL statement for renaming a DB table.
+ * @param string $oldName the table to be renamed. The name will be properly quoted by the method.
+ * @param string $newName the new table name. The name will be properly quoted by the method.
+ * @return string the SQL statement for renaming a DB table.
+ */
+ public function renameTable($oldName, $newName)
+ {
+ return 'ALTER TABLE ' . $this->db->quoteTableName($oldName) . ' RENAME TO ' . $this->db->quoteTableName($newName);
+ }
+
+ /**
+ * Builds a SQL statement for changing the definition of a column.
+ * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
+ * @param string $column the name of the column to be changed. The name will be properly quoted by the method.
+ * @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract
+ * column type (if any) into the physical one. Anything that is not recognized as abstract type will be kept
+ * in the generated SQL. For example, 'string' will be turned into 'varchar(255)', while 'string not null'
+ * will become 'varchar(255) not null'.
+ * @return string the SQL statement for changing the definition of a column.
+ */
+ public function alterColumn($table, $column, $type)
+ {
+ return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN '
+ . $this->db->quoteColumnName($column) . ' TYPE '
+ . $this->getColumnType($type);
+ }
}
diff --git a/framework/yii/db/pgsql/Schema.php b/framework/yii/db/pgsql/Schema.php
index 94f845f..96889ab 100644
--- a/framework/yii/db/pgsql/Schema.php
+++ b/framework/yii/db/pgsql/Schema.php
@@ -31,9 +31,10 @@ class Schema extends \yii\db\Schema
* @var array mapping from physical column types (keys) to abstract
* column types (values)
*/
- public $typeMap = array(
+ public $typeMap = [
'abstime' => self::TYPE_TIMESTAMP,
'bit' => self::TYPE_STRING,
+ 'bool' => self::TYPE_BOOLEAN,
'boolean' => self::TYPE_BOOLEAN,
'box' => self::TYPE_STRING,
'character' => self::TYPE_STRING,
@@ -43,9 +44,12 @@ class Schema extends \yii\db\Schema
'circle' => self::TYPE_STRING,
'date' => self::TYPE_DATE,
'real' => self::TYPE_FLOAT,
+ 'decimal' => self::TYPE_DECIMAL,
'double precision' => self::TYPE_DECIMAL,
'inet' => self::TYPE_STRING,
'smallint' => self::TYPE_SMALLINT,
+ 'int4' => self::TYPE_INTEGER,
+ 'int8' => self::TYPE_BIGINT,
'integer' => self::TYPE_INTEGER,
'bigint' => self::TYPE_BIGINT,
'interval' => self::TYPE_STRING,
@@ -55,7 +59,6 @@ class Schema extends \yii\db\Schema
'money' => self::TYPE_MONEY,
'name' => self::TYPE_STRING,
'numeric' => self::TYPE_STRING,
- 'numrange' => self::TYPE_DECIMAL,
'oid' => self::TYPE_BIGINT, // should not be used. it's pg internal!
'path' => self::TYPE_STRING,
'point' => self::TYPE_STRING,
@@ -70,7 +73,7 @@ class Schema extends \yii\db\Schema
'bit varying' => self::TYPE_STRING,
'character varying' => self::TYPE_STRING,
'xml' => self::TYPE_STRING
- );
+ ];
/**
* Creates a query builder for the PostgreSQL database.
@@ -129,6 +132,52 @@ class Schema extends \yii\db\Schema
}
/**
+ * Determines the PDO type for the given PHP data value.
+ * @param mixed $data the data whose PDO type is to be determined
+ * @return integer the PDO type
+ * @see http://www.php.net/manual/en/pdo.constants.php
+ */
+ public function getPdoType($data)
+ {
+ // php type => PDO type
+ static $typeMap = [
+ // https://github.com/yiisoft/yii2/issues/1115
+ // Cast boolean to integer values to work around problems with PDO casting false to string '' https://bugs.php.net/bug.php?id=33876
+ 'boolean' => \PDO::PARAM_INT,
+ 'integer' => \PDO::PARAM_INT,
+ 'string' => \PDO::PARAM_STR,
+ 'resource' => \PDO::PARAM_LOB,
+ 'NULL' => \PDO::PARAM_NULL,
+ ];
+ $type = gettype($data);
+ return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
+ }
+
+ /**
+ * Returns all table names in the database.
+ * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
+ * @return array all table names in the database. The names have NO schema name prefix.
+ */
+ protected function findTableNames($schema = '')
+ {
+ if ($schema === '') {
+ $schema = $this->defaultSchema;
+ }
+ $sql = <<db->createCommand($sql);
+ $command->bindParam(':schema', $schema);
+ $rows = $command->queryAll();
+ $names = [];
+ foreach ($rows as $row) {
+ $names[] = $row['table_name'];
+ }
+ return $names;
+ }
+
+ /**
* Collects the foreign key column details for the given table.
* @param TableSchema $table the table metadata
*/
@@ -142,13 +191,13 @@ class Schema extends \yii\db\Schema
//http://www.postgresql.org/message-id/26677.1086673982@sss.pgh.pa.us
$sql = << $column) {
- $citem[] = array($fcolumns[$idx] => $column);
+ $citem[$column] = $fcolumns[$idx];
}
$table->foreignKeys[] = $citem;
}
}
/**
+ * Gets information about given table unique indexes.
+ * @param TableSchema $table the table metadata
+ * @return array with index names, columns and if it is an expression tree
+ */
+ protected function getUniqueIndexInformation($table)
+ {
+ $tableName = $this->quoteValue($table->name);
+ $tableSchema = $this->quoteValue($table->schemaName);
+
+ $sql = <<db->createCommand($sql)->queryAll();
+ }
+
+ /**
+ * Returns all unique indexes for the given table.
+ * Each array element is of the following structure:
+ *
+ * ~~~
+ * [
+ * 'IndexName1' => ['col1' [, ...]],
+ * 'IndexName2' => ['col2' [, ...]],
+ * ]
+ * ~~~
+ *
+ * @param TableSchema $table the table metadata
+ * @return array all unique indexes for the given table.
+ */
+ public function findUniqueIndexes($table)
+ {
+ $indexes = $this->getUniqueIndexInformation($table);
+ $uniqueIndexes = [];
+
+ foreach ($indexes as $index) {
+ $indexName = $index['indexname'];
+
+ if ($index['indexprs']) {
+ // Index is an expression like "lower(colname::text)"
+ $indexColumns = preg_replace("/.*\(([^\:]+).*/mi", "$1", $index['indexcolumns']);
+ } else {
+ $indexColumns = array_map('trim', explode(',', str_replace(['{', '}'], '', $index['indexcolumns'])));
+ }
+
+ $uniqueIndexes[$indexName] = $indexColumns;
+
+ }
+ return $uniqueIndexes;
+ }
+
+ /**
* Collects the metadata of table columns.
* @param TableSchema $table the table metadata
* @return boolean whether the table exists in the database
@@ -187,19 +304,18 @@ SQL;
$tableName = $this->db->quoteValue($table->name);
$schemaName = $this->db->quoteValue($table->schemaName);
$sql = << 0
+ a.attnum > 0 and t.typname != ''
and c.relname = {$tableName}
and d.nspname = {$schemaName}
ORDER BY
@@ -243,13 +359,16 @@ ORDER BY
SQL;
$columns = $this->db->createCommand($sql)->queryAll();
+ if (empty($columns)) {
+ return false;
+ }
foreach ($columns as $column) {
$column = $this->loadColumnSchema($column);
$table->columns[$column->name] = $column;
if ($column->isPrimaryKey === true) {
$table->primaryKey[] = $column->name;
- if ($table->sequenceName === null && preg_match("/nextval\('\w+'(::regclass)?\)/", $column->defaultValue) === 1) {
- $table->sequenceName = preg_replace(array('/nextval/', '/::/', '/regclass/', '/\'\)/', '/\(\'/'), '', $column->defaultValue);
+ if ($table->sequenceName === null && preg_match("/nextval\\('\"?\\w+\"?\.?\"?\\w+\"?'(::regclass)?\\)/", $column->defaultValue) === 1) {
+ $table->sequenceName = preg_replace(['/nextval/', '/::/', '/regclass/', '/\'\)/', '/\(\'/'], '', $column->defaultValue);
}
}
}
@@ -269,8 +388,8 @@ SQL;
$column->comment = $info['column_comment'];
$column->dbType = $info['data_type'];
$column->defaultValue = $info['column_default'];
- $column->enumValues = explode(',', str_replace(array("''"), array("'"), $info['enum_values']));
- $column->unsigned = false; // has no meanining in PG
+ $column->enumValues = explode(',', str_replace(["''"], ["'"], $info['enum_values']));
+ $column->unsigned = false; // has no meaning in PG
$column->isPrimaryKey = $info['is_pkey'];
$column->name = $info['column_name'];
$column->precision = $info['numeric_precision'];
diff --git a/framework/yii/db/sqlite/QueryBuilder.php b/framework/yii/db/sqlite/QueryBuilder.php
index 52c101b..bddc436 100644
--- a/framework/yii/db/sqlite/QueryBuilder.php
+++ b/framework/yii/db/sqlite/QueryBuilder.php
@@ -22,8 +22,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
/**
* @var array mapping from abstract column types (keys) to physical column types (values).
*/
- public $typeMap = array(
+ public $typeMap = [
Schema::TYPE_PK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
+ Schema::TYPE_BIGPK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
Schema::TYPE_STRING => 'varchar(255)',
Schema::TYPE_TEXT => 'text',
Schema::TYPE_SMALLINT => 'smallint',
@@ -38,7 +39,54 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_BINARY => 'blob',
Schema::TYPE_BOOLEAN => 'boolean',
Schema::TYPE_MONEY => 'decimal(19,4)',
- );
+ ];
+
+ /**
+ * Generates a batch INSERT SQL statement.
+ * For example,
+ *
+ * ~~~
+ * $connection->createCommand()->batchInsert('tbl_user', ['name', 'age'], [
+ * ['Tom', 30],
+ * ['Jane', 20],
+ * ['Linda', 25],
+ * ])->execute();
+ * ~~~
+ *
+ * Note that the values in each row must match the corresponding column names.
+ *
+ * @param string $table the table that new rows will be inserted into.
+ * @param array $columns the column names
+ * @param array $rows the rows to be batch inserted into the table
+ * @return string the batch INSERT SQL statement
+ */
+ public function batchInsert($table, $columns, $rows)
+ {
+ if (($tableSchema = $this->db->getTableSchema($table)) !== null) {
+ $columnSchemas = $tableSchema->columns;
+ } else {
+ $columnSchemas = [];
+ }
+
+ foreach ($columns as $i => $name) {
+ $columns[$i] = $this->db->quoteColumnName($name);
+ }
+
+ $values = [];
+ foreach ($rows as $row) {
+ $vs = [];
+ foreach ($row as $i => $value) {
+ if (!is_array($value) && isset($columnSchemas[$columns[$i]])) {
+ $value = $columnSchemas[$columns[$i]]->typecast($value);
+ }
+ $vs[] = is_string($value) ? $this->db->quoteValue($value) : $value;
+ }
+ $values[] = implode(', ', $vs);
+ }
+
+ return 'INSERT INTO ' . $this->db->quoteTableName($table)
+ . ' (' . implode(', ', $columns) . ') SELECT ' . implode(' UNION ALL ', $values);
+ }
/**
* Creates a SQL statement for resetting the sequence value of a table's primary key.
@@ -79,6 +127,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
* @param boolean $check whether to turn on or off the integrity check.
* @param string $schema the schema of the tables. Meaningless for SQLite.
* @param string $table the table name. Meaningless for SQLite.
+ * @return string the SQL statement for checking integrity
* @throws NotSupportedException this is not supported by SQLite
*/
public function checkIntegrity($check = true, $schema = '', $table = '')
@@ -179,4 +228,48 @@ class QueryBuilder extends \yii\db\QueryBuilder
{
throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
}
+
+ /**
+ * Builds a SQL statement for adding a primary key constraint to an existing table.
+ * @param string $name the name of the primary key constraint.
+ * @param string $table the table that the primary key constraint will be added to.
+ * @param string|array $columns comma separated string or array of columns that the primary key will consist of.
+ * @return string the SQL statement for adding a primary key constraint to an existing table.
+ * @throws NotSupportedException this is not supported by SQLite
+ */
+ public function addPrimaryKey($name, $table, $columns)
+ {
+ throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
+ }
+
+ /**
+ * Builds a SQL statement for removing a primary key constraint to an existing table.
+ * @param string $name the name of the primary key constraint to be removed.
+ * @param string $table the table that the primary key constraint will be removed from.
+ * @return string the SQL statement for removing a primary key constraint from an existing table.
+ * @throws NotSupportedException this is not supported by SQLite *
+ */
+ public function dropPrimaryKey($name, $table)
+ {
+ throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function buildLimit($limit, $offset)
+ {
+ $sql = '';
+ // limit is not optional in SQLite
+ // http://www.sqlite.org/syntaxdiagrams.html#select-stmt
+ if ($limit !== null && $limit >= 0) {
+ $sql = 'LIMIT ' . (int)$limit;
+ if ($offset > 0) {
+ $sql .= ' OFFSET ' . (int)$offset;
+ }
+ } elseif ($offset > 0) {
+ $sql = 'LIMIT 9223372036854775807 OFFSET ' . (int)$offset; // 2^63-1
+ }
+ return $sql;
+ }
}
diff --git a/framework/yii/db/sqlite/Schema.php b/framework/yii/db/sqlite/Schema.php
index d4fb245..9f410b4 100644
--- a/framework/yii/db/sqlite/Schema.php
+++ b/framework/yii/db/sqlite/Schema.php
@@ -21,7 +21,7 @@ class Schema extends \yii\db\Schema
/**
* @var array mapping from physical column types (keys) to abstract column types (values)
*/
- public $typeMap = array(
+ public $typeMap = [
'tinyint' => self::TYPE_SMALLINT,
'bit' => self::TYPE_SMALLINT,
'smallint' => self::TYPE_SMALLINT,
@@ -47,7 +47,29 @@ class Schema extends \yii\db\Schema
'time' => self::TYPE_TIME,
'timestamp' => self::TYPE_TIMESTAMP,
'enum' => self::TYPE_STRING,
- );
+ ];
+
+ /**
+ * Quotes a table name for use in a query.
+ * A simple table name has no schema prefix.
+ * @param string $name table name
+ * @return string the properly quoted table name
+ */
+ public function quoteSimpleTableName($name)
+ {
+ return strpos($name, "`") !== false ? $name : "`" . $name . "`";
+ }
+
+ /**
+ * Quotes a column name for use in a query.
+ * A simple column name has no prefix.
+ * @param string $name column name
+ * @return string the properly quoted column name
+ */
+ public function quoteSimpleColumnName($name)
+ {
+ return strpos($name, '`') !== false || $name === '*' ? $name : '`' . $name . '`';
+ }
/**
* Creates a query builder for the MySQL database.
@@ -126,8 +148,48 @@ class Schema extends \yii\db\Schema
$sql = "PRAGMA foreign_key_list(" . $this->quoteSimpleTableName($table->name) . ')';
$keys = $this->db->createCommand($sql)->queryAll();
foreach ($keys as $key) {
- $table->foreignKeys[] = array($key['table'], $key['from'] => $key['to']);
+ $id = (int)$key['id'];
+ if (!isset($table->foreignKeys[$id])) {
+ $table->foreignKeys[$id] = [$key['table'], $key['from'] => $key['to']];
+ } else {
+ // composite FK
+ $table->foreignKeys[$id][$key['from']] = $key['to'];
+ }
+ }
+ }
+
+ /**
+ * Returns all unique indexes for the given table.
+ * Each array element is of the following structure:
+ *
+ * ~~~
+ * [
+ * 'IndexName1' => ['col1' [, ...]],
+ * 'IndexName2' => ['col2' [, ...]],
+ * ]
+ * ~~~
+ *
+ * @param TableSchema $table the table metadata
+ * @return array all unique indexes for the given table.
+ */
+ public function findUniqueIndexes($table)
+ {
+ $sql = "PRAGMA index_list(" . $this->quoteSimpleTableName($table->name) . ')';
+ $indexes = $this->db->createCommand($sql)->queryAll();
+ $uniqueIndexes = [];
+
+ foreach ($indexes as $index) {
+ $indexName = $index['name'];
+ $indexInfo = $this->db->createCommand("PRAGMA index_info(" . $this->quoteValue($index['name']) . ")")->queryAll();
+
+ if ($index['unique']) {
+ $uniqueIndexes[$indexName] = [];
+ foreach ($indexInfo as $row) {
+ $uniqueIndexes[$indexName][] = $row['name'];
+ }
+ }
}
+ return $uniqueIndexes;
}
/**
@@ -147,7 +209,7 @@ class Schema extends \yii\db\Schema
$column->type = self::TYPE_STRING;
if (preg_match('/^(\w+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) {
- $type = $matches[1];
+ $type = strtolower($matches[1]);
if (isset($this->typeMap[$type])) {
$column->type = $this->typeMap[$type];
}
diff --git a/framework/yii/debug/Module.php b/framework/yii/debug/Module.php
deleted file mode 100644
index a680f53..0000000
--- a/framework/yii/debug/Module.php
+++ /dev/null
@@ -1,17 +0,0 @@
-
- * @since 2.0
- */
-class Module extends \yii\base\Module
-{
- public $controllerNamespace = 'yii\debug\controllers';
-}
diff --git a/framework/yii/debug/Toolbar.php b/framework/yii/debug/Toolbar.php
deleted file mode 100644
index c205277..0000000
--- a/framework/yii/debug/Toolbar.php
+++ /dev/null
@@ -1,38 +0,0 @@
-
- * @since 2.0
- */
-class Toolbar extends Widget
-{
- public $debugAction = 'debug/default/toolbar';
-
- public function run()
- {
- if (Yii::$app->hasModule('debug')) {
- $id = 'yii-debug-toolbar';
- $url = Yii::$app->getUrlManager()->createUrl($this->debugAction, array(
- 'tag' => Yii::getLogger()->tag,
- ));
- $view = $this->getView();
- $view->registerJs("yii.debug.load('$id', '$url');");
- $view->registerAssetBundle('yii/debug');
- echo Html::tag('div', '', array(
- 'id' => $id,
- 'style' => 'display: none',
- ));
- }
- }
-}
diff --git a/framework/yii/debug/controllers/DefaultController.php b/framework/yii/debug/controllers/DefaultController.php
deleted file mode 100644
index f1160b1..0000000
--- a/framework/yii/debug/controllers/DefaultController.php
+++ /dev/null
@@ -1,34 +0,0 @@
-
- * @since 2.0
- */
-class DefaultController extends Controller
-{
- public function actionIndex($tag)
- {
- echo $tag;
- }
-
- public function actionToolbar($tag)
- {
- $file = Yii::$app->getRuntimePath() . "/debug/$tag.log";
- if (preg_match('/^[\w\-]+$/', $tag) && is_file($file)) {
- $data = json_decode(file_get_contents($file), true);
- echo $this->renderPartial('toolbar', $data);
- } else {
- echo "Unable to find debug data tagged with '$tag'.";
- }
- }
-}
diff --git a/framework/yii/debug/views/default/toolbar.php b/framework/yii/debug/views/default/toolbar.php
deleted file mode 100644
index 0b08d4b..0000000
--- a/framework/yii/debug/views/default/toolbar.php
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-Peak memory:
-
-
-
-Time spent:
-
-
-
-
-
-
-
-
diff --git a/framework/yii/grid/ActionColumn.php b/framework/yii/grid/ActionColumn.php
new file mode 100644
index 0000000..e97f535
--- /dev/null
+++ b/framework/yii/grid/ActionColumn.php
@@ -0,0 +1,135 @@
+
+ * @since 2.0
+ */
+class ActionColumn extends Column
+{
+ /**
+ * @var string the ID of the controller that should handle the actions specified here.
+ * If not set, it will use the currently active controller. This property is mainly used by
+ * [[urlCreator]] to create URLs for different actions. The value of this property will be prefixed
+ * to each action name to form the route of the action.
+ */
+ public $controller;
+ /**
+ * @var string the template used for composing each cell in the action column.
+ * Tokens enclosed within curly brackets are treated as controller action IDs (also called *button names*
+ * in the context of action column). They will be replaced by the corresponding button rendering callbacks
+ * specified in [[buttons]]. For example, the token `{view}` will be replaced by the result of
+ * the callback `buttons['view']`. If a callback cannot be found, the token will be replaced with an empty string.
+ * @see buttons
+ */
+ public $template = '{view} {update} {delete}';
+ /**
+ * @var array button rendering callbacks. The array keys are the button names (without curly brackets),
+ * and the values are the corresponding button rendering callbacks. The callbacks should use the following
+ * signature:
+ *
+ * ```php
+ * function ($url, $model) {
+ * // return the button HTML code
+ * }
+ * ```
+ *
+ * where `$url` is the URL that the column creates for the button, and `$model` is the model object
+ * being rendered for the current row.
+ */
+ public $buttons = [];
+ /**
+ * @var callback a callback that creates a button URL using the specified model information.
+ * The signature of the callback should be the same as that of [[createUrl()]].
+ * If this property is not set, button URLs will be created using [[createUrl()]].
+ */
+ public $urlCreator;
+
+
+ /**
+ * @inheritdoc
+ */
+ public function init()
+ {
+ parent::init();
+ $this->initDefaultButtons();
+ }
+
+ /**
+ * Initializes the default button rendering callbacks
+ */
+ protected function initDefaultButtons()
+ {
+ if (!isset($this->buttons['view'])) {
+ $this->buttons['view'] = function ($url, $model) {
+ return Html::a(' ', $url, [
+ 'title' => Yii::t('yii', 'View'),
+ ]);
+ };
+ }
+ if (!isset($this->buttons['update'])) {
+ $this->buttons['update'] = function ($url, $model) {
+ return Html::a(' ', $url, [
+ 'title' => Yii::t('yii', 'Update'),
+ ]);
+ };
+ }
+ if (!isset($this->buttons['delete'])) {
+ $this->buttons['delete'] = function ($url, $model) {
+ return Html::a(' ', $url, [
+ 'title' => Yii::t('yii', 'Delete'),
+ 'data-confirm' => Yii::t('yii', 'Are you sure to delete this item?'),
+ 'data-method' => 'post',
+ ]);
+ };
+ }
+ }
+
+ /**
+ * Creates a URL for the given action and model.
+ * This method is called for each button and each row.
+ * @param string $action the button name (or action ID)
+ * @param \yii\db\ActiveRecord $model the data model
+ * @param mixed $key the key associated with the data model
+ * @param integer $index the current row index
+ * @return string the created URL
+ */
+ public function createUrl($action, $model, $key, $index)
+ {
+ if ($this->urlCreator instanceof Closure) {
+ return call_user_func($this->urlCreator, $action, $model, $key, $index);
+ } else {
+ $params = is_array($key) ? $key : ['id' => $key];
+ $route = $this->controller ? $this->controller . '/' . $action : $action;
+ return Yii::$app->controller->createUrl($route, $params);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function renderDataCellContent($model, $key, $index)
+ {
+ return preg_replace_callback('/\\{(\w+)\\}/', function ($matches) use ($model, $key, $index) {
+ $name = $matches[1];
+ if (isset($this->buttons[$name])) {
+ $url = $this->createUrl($name, $model, $key, $index);
+ return call_user_func($this->buttons[$name], $url, $model);
+ } else {
+ return '';
+ }
+ }, $this->template);
+ }
+}
diff --git a/framework/yii/grid/CheckboxColumn.php b/framework/yii/grid/CheckboxColumn.php
new file mode 100644
index 0000000..6970d4b
--- /dev/null
+++ b/framework/yii/grid/CheckboxColumn.php
@@ -0,0 +1,81 @@
+
+ * @since 2.0
+ */
+class CheckboxColumn extends Column
+{
+ public $name = 'selection';
+ public $checkboxOptions = [];
+ public $multiple = true;
+
+
+ public function init()
+ {
+ parent::init();
+ if (empty($this->name)) {
+ throw new InvalidConfigException('The "name" property must be set.');
+ }
+ if (substr($this->name, -2) !== '[]') {
+ $this->name .= '[]';
+ }
+ }
+
+ /**
+ * Renders the header cell content.
+ * The default implementation simply renders [[header]].
+ * This method may be overridden to customize the rendering of the header cell.
+ * @return string the rendering result
+ */
+ protected function renderHeaderCellContent()
+ {
+ $name = rtrim($this->name, '[]') . '_all';
+ $id = $this->grid->options['id'];
+ $options = json_encode([
+ 'name' => $this->name,
+ 'multiple' => $this->multiple,
+ 'checkAll' => $name,
+ ]);
+ $this->grid->getView()->registerJs("jQuery('#$id').yiiGridView('setSelectionColumn', $options);");
+
+ if ($this->header !== null || !$this->multiple) {
+ return parent::renderHeaderCellContent();
+ } else {
+ return Html::checkBox($name, false, ['class' => 'select-on-check-all']);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function renderDataCellContent($model, $key, $index)
+ {
+ if ($this->checkboxOptions instanceof Closure) {
+ $options = call_user_func($this->checkboxOptions, $model, $key, $index, $this);
+ } else {
+ $options = $this->checkboxOptions;
+ }
+ return Html::checkbox($this->name, !empty($options['checked']), $options);
+ }
+}
diff --git a/framework/yii/grid/Column.php b/framework/yii/grid/Column.php
new file mode 100644
index 0000000..5cc4c42
--- /dev/null
+++ b/framework/yii/grid/Column.php
@@ -0,0 +1,144 @@
+
+ * @since 2.0
+ */
+class Column extends Object
+{
+ /**
+ * @var GridView the grid view object that owns this column.
+ */
+ public $grid;
+ /**
+ * @var string the header cell content. Note that it will not be HTML-encoded.
+ */
+ public $header;
+ /**
+ * @var string the footer cell content. Note that it will not be HTML-encoded.
+ */
+ public $footer;
+ /**
+ * @var callable
+ */
+ public $content;
+ /**
+ * @var boolean whether this column is visible. Defaults to true.
+ */
+ public $visible = true;
+ public $options = [];
+ public $headerOptions = [];
+ /**
+ * @var array|\Closure
+ */
+ public $contentOptions = [];
+ public $footerOptions = [];
+ /**
+ * @var array the HTML attributes for the filter cell tag.
+ */
+ public $filterOptions=[];
+
+
+ /**
+ * Renders the header cell.
+ */
+ public function renderHeaderCell()
+ {
+ return Html::tag('th', $this->renderHeaderCellContent(), $this->headerOptions);
+ }
+
+ /**
+ * Renders the footer cell.
+ */
+ public function renderFooterCell()
+ {
+ return Html::tag('td', $this->renderFooterCellContent(), $this->footerOptions);
+ }
+
+ /**
+ * Renders a data cell.
+ * @param mixed $model the data model being rendered
+ * @param mixed $key the key associated with the data model
+ * @param integer $index the zero-based index of the data item among the item array returned by [[dataProvider]].
+ * @return string the rendering result
+ */
+ public function renderDataCell($model, $key, $index)
+ {
+ if ($this->contentOptions instanceof Closure) {
+ $options = call_user_func($this->contentOptions, $model, $key, $index, $this);
+ } else {
+ $options = $this->contentOptions;
+ }
+ return Html::tag('td', $this->renderDataCellContent($model, $key, $index), $options);
+ }
+
+ /**
+ * Renders the filter cell.
+ */
+ public function renderFilterCell()
+ {
+ return Html::tag('td', $this->renderFilterCellContent(), $this->filterOptions);
+ }
+
+ /**
+ * Renders the header cell content.
+ * The default implementation simply renders [[header]].
+ * This method may be overridden to customize the rendering of the header cell.
+ * @return string the rendering result
+ */
+ protected function renderHeaderCellContent()
+ {
+ return trim($this->header) !== '' ? $this->header : $this->grid->emptyCell;
+ }
+
+ /**
+ * Renders the footer cell content.
+ * The default implementation simply renders [[footer]].
+ * This method may be overridden to customize the rendering of the footer cell.
+ * @return string the rendering result
+ */
+ protected function renderFooterCellContent()
+ {
+ return trim($this->footer) !== '' ? $this->footer : $this->grid->emptyCell;
+ }
+
+ /**
+ * Renders the data cell content.
+ * @param mixed $model the data model
+ * @param mixed $key the key associated with the data model
+ * @param integer $index the zero-based index of the data model among the models array returned by [[dataProvider]].
+ * @return string the rendering result
+ */
+ protected function renderDataCellContent($model, $key, $index)
+ {
+ if ($this->content !== null) {
+ return call_user_func($this->content, $model, $key, $index, $this);
+ } else {
+ return $this->grid->emptyCell;
+ }
+ }
+
+ /**
+ * Renders the filter cell content.
+ * The default implementation simply renders a space.
+ * This method may be overridden to customize the rendering of the filter cell (if any).
+ * @return string the rendering result
+ */
+ protected function renderFilterCellContent()
+ {
+ return $this->grid->emptyCell;
+ }
+}
diff --git a/framework/yii/grid/DataColumn.php b/framework/yii/grid/DataColumn.php
new file mode 100644
index 0000000..032c21c
--- /dev/null
+++ b/framework/yii/grid/DataColumn.php
@@ -0,0 +1,151 @@
+
+ * @since 2.0
+ */
+class DataColumn extends Column
+{
+ /**
+ * @var string the attribute name associated with this column. When neither [[content]] nor [[value]]
+ * is specified, the value of the specified attribute will be retrieved from each data model and displayed.
+ *
+ * Also, if [[header]] is not specified, the label associated with the attribute will be displayed.
+ */
+ public $attribute;
+ /**
+ * @var string label to be displayed in the [[header|header cell]] and also to be used as the sorting
+ * link label when sorting is enabled for this column.
+ * If it is not set and the models provided by the GridViews data provider are instances
+ * of [[yii\db\ActiveRecord]], the label will be determined using [[yii\db\ActiveRecord::getAttributeLabel()]].
+ * Otherwise [[yii\helpers\Inflector::camel2words()]] will be used to get a label.
+ */
+ public $label;
+ /**
+ * @var \Closure an anonymous function that returns the value to be displayed for every data model.
+ * The signature of this function is `function ($model, $index, $widget)`.
+ * If this is not set, `$model[$attribute]` will be used to obtain the value.
+ */
+ public $value;
+ /**
+ * @var string|array in which format should the value of each data model be displayed as (e.g. "text", "html",
+ * ['date', 'Y-m-d']). Supported formats are determined by the [[GridView::formatter|formatter]] used by
+ * the [[GridView]]. Default format is "text" which will format the value as an HTML-encoded plain text when
+ * [[\yii\base\Formatter::format()]] or [[\yii\i18n\Formatter::format()]] is used.
+ */
+ public $format = 'text';
+ /**
+ * @var boolean whether to allow sorting by this column. If true and [[attribute]] is found in
+ * the sort definition of [[GridView::dataProvider]], then the header cell of this column
+ * will contain a link that may trigger the sorting when being clicked.
+ */
+ public $enableSorting = true;
+ /**
+ * @var array the HTML attributes for the link tag in the header cell
+ * generated by [[Sort::link]] when sorting is enabled for this column.
+ */
+ public $sortLinkOptions = [];
+ /**
+ * @var string|array|boolean the HTML code representing a filter input (e.g. a text field, a dropdown list)
+ * that is used for this data column. This property is effective only when [[GridView::filterModel]] is set.
+ *
+ * - If this property is not set, a text field will be generated as the filter input;
+ * - If this property is an array, a dropdown list will be generated that uses this property value as
+ * the list options.
+ * - If you don't want a filter for this data column, set this value to be false.
+ */
+ public $filter;
+ /**
+ * @var array the HTML attributes for the filter input fields. This property is used in combination with
+ * the [[filter]] property. When [[filter]] is not set or is an array, this property will be used to
+ * render the HTML attributes for the generated filter input fields.
+ */
+ public $filterInputOptions = ['class' => 'form-control', 'id' => null];
+
+
+ protected function renderHeaderCellContent()
+ {
+ if ($this->header !== null || $this->label === null && $this->attribute === null) {
+ return parent::renderHeaderCellContent();
+ }
+
+ $provider = $this->grid->dataProvider;
+
+ if ($this->label === null) {
+ if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQuery) {
+ /** @var Model $model */
+ $model = new $provider->query->modelClass;
+ $label = $model->getAttributeLabel($this->attribute);
+ } else {
+ $models = $provider->getModels();
+ if (($model = reset($models)) instanceof Model) {
+ /** @var Model $model */
+ $label = $model->getAttributeLabel($this->attribute);
+ } else {
+ $label = Inflector::camel2words($this->attribute);
+ }
+ }
+ } else {
+ $label = $this->label;
+ }
+
+ if ($this->attribute !== null && $this->enableSorting &&
+ ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) {
+
+ return $sort->link($this->attribute, array_merge($this->sortLinkOptions, ['label' => Html::encode($label)]));
+ } else {
+ return Html::encode($label);
+ }
+ }
+
+ protected function renderFilterCellContent()
+ {
+ if (is_string($this->filter)) {
+ return $this->filter;
+ } elseif ($this->filter !== false && $this->grid->filterModel instanceof Model &&
+ $this->attribute !== null && $this->grid->filterModel->isAttributeActive($this->attribute))
+ {
+ if (is_array($this->filter)) {
+ $options = array_merge(['prompt' => ''], $this->filterInputOptions);
+ return Html::activeDropDownList($this->grid->filterModel, $this->attribute, $this->filter, $options);
+ } else {
+ return Html::activeTextInput($this->grid->filterModel, $this->attribute, $this->filterInputOptions);
+ }
+ } else {
+ return parent::renderFilterCellContent();
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function renderDataCellContent($model, $key, $index)
+ {
+ if ($this->value !== null) {
+ $value = call_user_func($this->value, $model, $index, $this);
+ } elseif ($this->content === null && $this->attribute !== null) {
+ $value = ArrayHelper::getValue($model, $this->attribute);
+ } else {
+ return parent::renderDataCellContent($model, $key, $index);
+ }
+ return $this->grid->formatter->format($value, $this->format);
+ }
+}
diff --git a/framework/yii/grid/GridView.php b/framework/yii/grid/GridView.php
new file mode 100644
index 0000000..35d89b2
--- /dev/null
+++ b/framework/yii/grid/GridView.php
@@ -0,0 +1,437 @@
+
+ * @since 2.0
+ */
+class GridView extends BaseListView
+{
+ const FILTER_POS_HEADER = 'header';
+ const FILTER_POS_FOOTER = 'footer';
+ const FILTER_POS_BODY = 'body';
+
+ /**
+ * @var string the default data column class if the class name is not explicitly specified when configuring a data column.
+ * Defaults to 'yii\grid\DataColumn'.
+ */
+ public $dataColumnClass;
+ /**
+ * @var string the caption of the grid table
+ * @see captionOptions
+ */
+ public $caption;
+ /**
+ * @var array the HTML attributes for the caption element
+ * @see caption
+ */
+ public $captionOptions = [];
+ /**
+ * @var array the HTML attributes for the grid table element
+ */
+ public $tableOptions = ['class' => 'table table-striped table-bordered'];
+ /**
+ * @var array the HTML attributes for the table header row
+ */
+ public $headerRowOptions = [];
+ /**
+ * @var array the HTML attributes for the table footer row
+ */
+ public $footerRowOptions = [];
+ /**
+ * @var array|Closure the HTML attributes for the table body rows. This can be either an array
+ * specifying the common HTML attributes for all body rows, or an anonymous function that
+ * returns an array of the HTML attributes. The anonymous function will be called once for every
+ * data model returned by [[dataProvider]]. It should have the following signature:
+ *
+ * ~~~php
+ * function ($model, $key, $index, $grid)
+ * ~~~
+ *
+ * - `$model`: the current data model being rendered
+ * - `$key`: the key value associated with the current data model
+ * - `$index`: the zero-based index of the data model in the model array returned by [[dataProvider]]
+ * - `$grid`: the GridView object
+ */
+ public $rowOptions = [];
+ /**
+ * @var Closure an anonymous function that is called once BEFORE rendering each data model.
+ * It should have the similar signature as [[rowOptions]]. The return result of the function
+ * will be rendered directly.
+ */
+ public $beforeRow;
+ /**
+ * @var Closure an anonymous function that is called once AFTER rendering each data model.
+ * It should have the similar signature as [[rowOptions]]. The return result of the function
+ * will be rendered directly.
+ */
+ public $afterRow;
+ /**
+ * @var boolean whether to show the header section of the grid table.
+ */
+ public $showHeader = true;
+ /**
+ * @var boolean whether to show the footer section of the grid table.
+ */
+ public $showFooter = false;
+ /**
+ * @var boolean whether to show the grid view if [[dataProvider]] returns no data.
+ */
+ public $showOnEmpty = true;
+ /**
+ * @var array|Formatter the formatter used to format model attribute values into displayable texts.
+ * This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]]
+ * instance. If this property is not set, the "formatter" application component will be used.
+ */
+ public $formatter;
+ /**
+ * @var array grid column configuration. Each array element represents the configuration
+ * for one particular grid column. For example,
+ *
+ * ~~~php
+ * [
+ * ['class' => SerialColumn::className()],
+ * [
+ * 'class' => DataColumn::className(),
+ * 'attribute' => 'name',
+ * 'format' => 'text',
+ * 'label' => 'Name',
+ * ],
+ * ['class' => CheckboxColumn::className()],
+ * ]
+ * ~~~
+ *
+ * If a column is of class [[DataColumn]], the "class" element can be omitted.
+ *
+ * As a shortcut format, a string may be used to specify the configuration of a data column
+ * which only contains "attribute", "format", and/or "label" options: `"attribute:format:label"`.
+ * For example, the above "name" column can also be specified as: `"name:text:Name"`.
+ * Both "format" and "label" are optional. They will take default values if absent.
+ */
+ public $columns = [];
+ public $emptyCell = ' ';
+ /**
+ * @var \yii\base\Model the model that keeps the user-entered filter data. When this property is set,
+ * the grid view will enable column-based filtering. Each data column by default will display a text field
+ * at the top that users can fill in to filter the data.
+ *
+ * Note that in order to show an input field for filtering, a column must have its [[DataColumn::attribute]]
+ * property set or have [[DataColumn::filter]] set as the HTML code for the input field.
+ *
+ * When this property is not set (null) the filtering feature is disabled.
+ */
+ public $filterModel;
+ /**
+ * @var string|array the URL for returning the filtering result. [[Html::url()]] will be called to
+ * normalize the URL. If not set, the current controller action will be used.
+ * When the user makes change to any filter input, the current filtering inputs will be appended
+ * as GET parameters to this URL.
+ */
+ public $filterUrl;
+ public $filterSelector;
+ /**
+ * @var string whether the filters should be displayed in the grid view. Valid values include:
+ *
+ * - [[FILTER_POS_HEADER]]: the filters will be displayed on top of each column's header cell.
+ * - [[FILTER_POS_BODY]]: the filters will be displayed right below each column's header cell.
+ * - [[FILTER_POS_FOOTER]]: the filters will be displayed below each column's footer cell.
+ */
+ public $filterPosition = self::FILTER_POS_BODY;
+ /**
+ * @var array the HTML attributes for the filter row element
+ */
+ public $filterRowOptions = ['class' => 'filters'];
+
+ /**
+ * Initializes the grid view.
+ * This method will initialize required property values and instantiate [[columns]] objects.
+ */
+ public function init()
+ {
+ parent::init();
+ if ($this->formatter == null) {
+ $this->formatter = Yii::$app->getFormatter();
+ } elseif (is_array($this->formatter)) {
+ $this->formatter = Yii::createObject($this->formatter);
+ }
+ if (!$this->formatter instanceof Formatter) {
+ throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.');
+ }
+ if (!isset($this->options['id'])) {
+ $this->options['id'] = $this->getId();
+ }
+ if (!isset($this->filterRowOptions['id'])) {
+ $this->filterRowOptions['id'] = $this->options['id'] . '-filters';
+ }
+
+ $this->initColumns();
+ }
+
+ /**
+ * Runs the widget.
+ */
+ public function run()
+ {
+ $id = $this->options['id'];
+ $options = Json::encode($this->getClientOptions());
+ $view = $this->getView();
+ GridViewAsset::register($view);
+ $view->registerJs("jQuery('#$id').yiiGridView($options);");
+ parent::run();
+ }
+
+
+ /**
+ * Returns the options for the grid view JS widget.
+ * @return array the options
+ */
+ protected function getClientOptions()
+ {
+ $filterUrl = isset($this->filterUrl) ? $this->filterUrl : [Yii::$app->controller->action->id];
+ $id = $this->filterRowOptions['id'];
+ $filterSelector = "#$id input, #$id select";
+ if (isset($this->filterSelector)) {
+ $filterSelector .= ', ' . $this->filterSelector;
+ }
+
+ return [
+ 'filterUrl' => Html::url($filterUrl),
+ 'filterSelector' => $filterSelector,
+ ];
+ }
+
+ /**
+ * Renders the data models for the grid view.
+ */
+ public function renderItems()
+ {
+ $content = array_filter([
+ $this->renderCaption(),
+ $this->renderColumnGroup(),
+ $this->showHeader ? $this->renderTableHeader() : false,
+ $this->showFooter ? $this->renderTableFooter() : false,
+ $this->renderTableBody(),
+ ]);
+ return Html::tag('table', implode("\n", $content), $this->tableOptions);
+ }
+
+ public function renderCaption()
+ {
+ if (!empty($this->caption)) {
+ return Html::tag('caption', $this->caption, $this->captionOptions);
+ } else {
+ return false;
+ }
+ }
+
+ public function renderColumnGroup()
+ {
+ $requireColumnGroup = false;
+ foreach ($this->columns as $column) {
+ /** @var Column $column */
+ if (!empty($column->options)) {
+ $requireColumnGroup = true;
+ break;
+ }
+ }
+ if ($requireColumnGroup) {
+ $cols = [];
+ foreach ($this->columns as $column) {
+ $cols[] = Html::tag('col', '', $column->options);
+ }
+ return Html::tag('colgroup', implode("\n", $cols));
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Renders the table header.
+ * @return string the rendering result
+ */
+ public function renderTableHeader()
+ {
+ $cells = [];
+ foreach ($this->columns as $column) {
+ /** @var Column $column */
+ $cells[] = $column->renderHeaderCell();
+ }
+ $content = implode('', $cells);
+ if ($this->filterPosition == self::FILTER_POS_HEADER) {
+ $content = $this->renderFilters() . $content;
+ } elseif ($this->filterPosition == self::FILTER_POS_BODY) {
+ $content .= $this->renderFilters();
+ }
+ return "\n" . Html::tag('tr', $content, $this->headerRowOptions) . "\n ";
+ }
+
+ /**
+ * Renders the table footer.
+ * @return string the rendering result
+ */
+ public function renderTableFooter()
+ {
+ $cells = [];
+ foreach ($this->columns as $column) {
+ /** @var Column $column */
+ $cells[] = $column->renderFooterCell();
+ }
+ $content = implode('', $cells);
+ if ($this->filterPosition == self::FILTER_POS_FOOTER) {
+ $content .= $this->renderFilters();
+ }
+ return "\n" . Html::tag('tr', $content, $this->footerRowOptions) . "\n ";
+ }
+
+ /**
+ * Renders the filter.
+ */
+ public function renderFilters()
+ {
+ if ($this->filterModel !== null) {
+ $cells = [];
+ foreach ($this->columns as $column) {
+ /** @var Column $column */
+ $cells[] = $column->renderFilterCell();
+ }
+ return Html::tag('tr', implode('', $cells), $this->filterRowOptions);
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Renders the table body.
+ * @return string the rendering result
+ */
+ public function renderTableBody()
+ {
+ $models = array_values($this->dataProvider->getModels());
+ $keys = $this->dataProvider->getKeys();
+ $rows = [];
+ foreach ($models as $index => $model) {
+ $key = $keys[$index];
+ if ($this->beforeRow !== null) {
+ $row = call_user_func($this->beforeRow, $model, $key, $index, $this);
+ if (!empty($row)) {
+ $rows[] = $row;
+ }
+ }
+
+ $rows[] = $this->renderTableRow($model, $key, $index);
+
+ if ($this->afterRow !== null) {
+ $row = call_user_func($this->afterRow, $model, $key, $index, $this);
+ if (!empty($row)) {
+ $rows[] = $row;
+ }
+ }
+ }
+
+ if (empty($rows)) {
+ $colspan = count($this->columns);
+ return "\n" . $this->renderEmpty() . " \n ";
+ } else {
+ return "\n" . implode("\n", $rows) . "\n ";
+ }
+ }
+
+ /**
+ * Renders a table row with the given data model and key.
+ * @param mixed $model the data model to be rendered
+ * @param mixed $key the key associated with the data model
+ * @param integer $index the zero-based index of the data model among the model array returned by [[dataProvider]].
+ * @return string the rendering result
+ */
+ public function renderTableRow($model, $key, $index)
+ {
+ $cells = [];
+ /** @var Column $column */
+ foreach ($this->columns as $column) {
+ $cells[] = $column->renderDataCell($model, $key, $index);
+ }
+ if ($this->rowOptions instanceof Closure) {
+ $options = call_user_func($this->rowOptions, $model, $key, $index, $this);
+ } else {
+ $options = $this->rowOptions;
+ }
+ $options['data-key'] = is_array($key) ? json_encode($key) : $key;
+ return Html::tag('tr', implode('', $cells), $options);
+ }
+
+ /**
+ * Creates column objects and initializes them.
+ */
+ protected function initColumns()
+ {
+ if (empty($this->columns)) {
+ $this->guessColumns();
+ }
+ foreach ($this->columns as $i => $column) {
+ if (is_string($column)) {
+ $column = $this->createDataColumn($column);
+ } else {
+ $column = Yii::createObject(array_merge([
+ 'class' => $this->dataColumnClass ?: DataColumn::className(),
+ 'grid' => $this,
+ ], $column));
+ }
+ if (!$column->visible) {
+ unset($this->columns[$i]);
+ continue;
+ }
+ $this->columns[$i] = $column;
+ }
+ }
+
+ /**
+ * Creates a [[DataColumn]] object based on a string in the format of "attribute:format:label".
+ * @param string $text the column specification string
+ * @return DataColumn the column instance
+ * @throws InvalidConfigException if the column specification is invalid
+ */
+ protected function createDataColumn($text)
+ {
+ if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $text, $matches)) {
+ throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:label');
+ }
+ return Yii::createObject([
+ 'class' => $this->dataColumnClass ?: DataColumn::className(),
+ 'grid' => $this,
+ 'attribute' => $matches[1],
+ 'format' => isset($matches[3]) ? $matches[3] : 'text',
+ 'label' => isset($matches[5]) ? $matches[5] : null,
+ ]);
+ }
+
+ protected function guessColumns()
+ {
+ $models = $this->dataProvider->getModels();
+ $model = reset($models);
+ if (is_array($model) || is_object($model)) {
+ foreach ($model as $name => $value) {
+ $this->columns[] = $name;
+ }
+ } else {
+ throw new InvalidConfigException('Unable to generate columns from data.');
+ }
+ }
+}
diff --git a/framework/yii/grid/GridViewAsset.php b/framework/yii/grid/GridViewAsset.php
new file mode 100644
index 0000000..a67999d
--- /dev/null
+++ b/framework/yii/grid/GridViewAsset.php
@@ -0,0 +1,27 @@
+
+ * @since 2.0
+ */
+class GridViewAsset extends AssetBundle
+{
+ public $sourcePath = '@yii/assets';
+ public $js = [
+ 'yii.gridView.js',
+ ];
+ public $depends = [
+ 'yii\web\YiiAsset',
+ ];
+}
diff --git a/framework/yii/grid/SerialColumn.php b/framework/yii/grid/SerialColumn.php
new file mode 100644
index 0000000..8179ead
--- /dev/null
+++ b/framework/yii/grid/SerialColumn.php
@@ -0,0 +1,32 @@
+
+ * @since 2.0
+ */
+class SerialColumn extends Column
+{
+ public $header = '#';
+
+ /**
+ * @inheritdoc
+ */
+ protected function renderDataCellContent($model, $key, $index)
+ {
+ $pagination = $this->grid->dataProvider->getPagination();
+ if ($pagination !== false) {
+ return $pagination->getOffset() + $index + 1;
+ } else {
+ return $index + 1;
+ }
+ }
+}
diff --git a/framework/yii/helpers/ArrayHelper.php b/framework/yii/helpers/ArrayHelper.php
index d58341c..9d428f5 100644
--- a/framework/yii/helpers/ArrayHelper.php
+++ b/framework/yii/helpers/ArrayHelper.php
@@ -8,12 +8,12 @@
namespace yii\helpers;
/**
- * ArrayHelper provides additional array functionality you can use in your
+ * ArrayHelper provides additional array functionality that you can use in your
* application.
*
* @author Qiang Xue
* @since 2.0
*/
-class ArrayHelper extends base\ArrayHelper
+class ArrayHelper extends BaseArrayHelper
{
}
diff --git a/framework/yii/helpers/BaseArrayHelper.php b/framework/yii/helpers/BaseArrayHelper.php
new file mode 100644
index 0000000..ad8cb21
--- /dev/null
+++ b/framework/yii/helpers/BaseArrayHelper.php
@@ -0,0 +1,452 @@
+
+ * @since 2.0
+ */
+class BaseArrayHelper
+{
+ /**
+ * Converts an object or an array of objects into an array.
+ * @param object|array $object the object to be converted into an array
+ * @param array $properties a mapping from object class names to the properties that need to put into the resulting arrays.
+ * The properties specified for each class is an array of the following format:
+ *
+ * ~~~
+ * [
+ * 'app\models\Post' => [
+ * 'id',
+ * 'title',
+ * // the key name in array result => property name
+ * 'createTime' => 'create_time',
+ * // the key name in array result => anonymous function
+ * 'length' => function ($post) {
+ * return strlen($post->content);
+ * },
+ * ],
+ * ]
+ * ~~~
+ *
+ * The result of `ArrayHelper::toArray($post, $properties)` could be like the following:
+ *
+ * ~~~
+ * [
+ * 'id' => 123,
+ * 'title' => 'test',
+ * 'createTime' => '2013-01-01 12:00AM',
+ * 'length' => 301,
+ * ]
+ * ~~~
+ *
+ * @param boolean $recursive whether to recursively converts properties which are objects into arrays.
+ * @return array the array representation of the object
+ */
+ public static function toArray($object, $properties = [], $recursive = true)
+ {
+ if (!empty($properties) && is_object($object)) {
+ $className = get_class($object);
+ if (!empty($properties[$className])) {
+ $result = [];
+ foreach ($properties[$className] as $key => $name) {
+ if (is_int($key)) {
+ $result[$name] = $object->$name;
+ } else {
+ $result[$key] = static::getValue($object, $name);
+ }
+ }
+ return $result;
+ }
+ }
+ if ($object instanceof Arrayable) {
+ $object = $object->toArray();
+ if (!$recursive) {
+ return $object;
+ }
+ }
+ $result = [];
+ foreach ($object as $key => $value) {
+ if ($recursive && (is_array($value) || is_object($value))) {
+ $result[$key] = static::toArray($value, true);
+ } else {
+ $result[$key] = $value;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Merges two or more arrays into one recursively.
+ * If each array has an element with the same string key value, the latter
+ * will overwrite the former (different from array_merge_recursive).
+ * Recursive merging will be conducted if both arrays have an element of array
+ * type and are having the same key.
+ * For integer-keyed elements, the elements from the latter array will
+ * be appended to the former array.
+ * @param array $a array to be merged to
+ * @param array $b array to be merged from. You can specify additional
+ * arrays via third argument, fourth argument etc.
+ * @return array the merged array (the original arrays are not changed.)
+ */
+ public static function merge($a, $b)
+ {
+ $args = func_get_args();
+ $res = array_shift($args);
+ while (!empty($args)) {
+ $next = array_shift($args);
+ foreach ($next as $k => $v) {
+ if (is_integer($k)) {
+ isset($res[$k]) ? $res[] = $v : $res[$k] = $v;
+ } elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) {
+ $res[$k] = self::merge($res[$k], $v);
+ } else {
+ $res[$k] = $v;
+ }
+ }
+ }
+ return $res;
+ }
+
+ /**
+ * Retrieves the value of an array element or object property with the given key or property name.
+ * If the key does not exist in the array, the default value will be returned instead.
+ *
+ * Below are some usage examples,
+ *
+ * ~~~
+ * // working with array
+ * $username = \yii\helpers\ArrayHelper::getValue($_POST, 'username');
+ * // working with object
+ * $username = \yii\helpers\ArrayHelper::getValue($user, 'username');
+ * // working with anonymous function
+ * $fullName = \yii\helpers\ArrayHelper::getValue($user, function($user, $defaultValue) {
+ * return $user->firstName . ' ' . $user->lastName;
+ * });
+ * ~~~
+ *
+ * @param array|object $array array or object to extract value from
+ * @param string|\Closure $key key name of the array element, or property name of the object,
+ * or an anonymous function returning the value. The anonymous function signature should be:
+ * `function($array, $defaultValue)`.
+ * @param mixed $default the default value to be returned if the specified key does not exist
+ * @return mixed the value of the element if found, default value otherwise
+ */
+ public static function getValue($array, $key, $default = null)
+ {
+ if ($key instanceof \Closure) {
+ return $key($array, $default);
+ } elseif (is_array($array)) {
+ return isset($array[$key]) || array_key_exists($key, $array) ? $array[$key] : $default;
+ } else {
+ return $array->$key;
+ }
+ }
+
+ /**
+ * Removes an item from an array and returns the value. If the key does not exist in the array, the default value
+ * will be returned instead.
+ *
+ * Usage examples,
+ *
+ * ~~~
+ * // $array = ['type' => 'A', 'options' => [1, 2]];
+ * // working with array
+ * $type = \yii\helpers\ArrayHelper::remove($array, 'type');
+ * // $array content
+ * // $array = ['options' => [1, 2]];
+ * ~~~
+ *
+ * @param array $array the array to extract value from
+ * @param string $key key name of the array element
+ * @param mixed $default the default value to be returned if the specified key does not exist
+ * @return mixed|null the value of the element if found, default value otherwise
+ */
+ public static function remove(&$array, $key, $default = null)
+ {
+ if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) {
+ $value = $array[$key];
+ unset($array[$key]);
+ return $value;
+ }
+ return $default;
+ }
+
+ /**
+ * Indexes an array according to a specified key.
+ * The input array should be multidimensional or an array of objects.
+ *
+ * The key can be a key name of the sub-array, a property name of object, or an anonymous
+ * function which returns the key value given an array element.
+ *
+ * If a key value is null, the corresponding array element will be discarded and not put in the result.
+ *
+ * For example,
+ *
+ * ~~~
+ * $array = [
+ * ['id' => '123', 'data' => 'abc'],
+ * ['id' => '345', 'data' => 'def'],
+ * ];
+ * $result = ArrayHelper::index($array, 'id');
+ * // the result is:
+ * // [
+ * // '123' => ['id' => '123', 'data' => 'abc'],
+ * // '345' => ['id' => '345', 'data' => 'def'],
+ * // ]
+ *
+ * // using anonymous function
+ * $result = ArrayHelper::index($array, function ($element) {
+ * return $element['id'];
+ * });
+ * ~~~
+ *
+ * @param array $array the array that needs to be indexed
+ * @param string|\Closure $key the column name or anonymous function whose result will be used to index the array
+ * @return array the indexed array
+ */
+ public static function index($array, $key)
+ {
+ $result = [];
+ foreach ($array as $element) {
+ $value = static::getValue($element, $key);
+ $result[$value] = $element;
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the values of a specified column in an array.
+ * The input array should be multidimensional or an array of objects.
+ *
+ * For example,
+ *
+ * ~~~
+ * $array = [
+ * ['id' => '123', 'data' => 'abc'],
+ * ['id' => '345', 'data' => 'def'],
+ * ];
+ * $result = ArrayHelper::getColumn($array, 'id');
+ * // the result is: ['123', '345']
+ *
+ * // using anonymous function
+ * $result = ArrayHelper::getColumn($array, function ($element) {
+ * return $element['id'];
+ * });
+ * ~~~
+ *
+ * @param array $array
+ * @param string|\Closure $name
+ * @param boolean $keepKeys whether to maintain the array keys. If false, the resulting array
+ * will be re-indexed with integers.
+ * @return array the list of column values
+ */
+ public static function getColumn($array, $name, $keepKeys = true)
+ {
+ $result = [];
+ if ($keepKeys) {
+ foreach ($array as $k => $element) {
+ $result[$k] = static::getValue($element, $name);
+ }
+ } else {
+ foreach ($array as $element) {
+ $result[] = static::getValue($element, $name);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Builds a map (key-value pairs) from a multidimensional array or an array of objects.
+ * The `$from` and `$to` parameters specify the key names or property names to set up the map.
+ * Optionally, one can further group the map according to a grouping field `$group`.
+ *
+ * For example,
+ *
+ * ~~~
+ * $array = [
+ * ['id' => '123', 'name' => 'aaa', 'class' => 'x'],
+ * ['id' => '124', 'name' => 'bbb', 'class' => 'x'],
+ * ['id' => '345', 'name' => 'ccc', 'class' => 'y'],
+ * );
+ *
+ * $result = ArrayHelper::map($array, 'id', 'name');
+ * // the result is:
+ * // [
+ * // '123' => 'aaa',
+ * // '124' => 'bbb',
+ * // '345' => 'ccc',
+ * // ]
+ *
+ * $result = ArrayHelper::map($array, 'id', 'name', 'class');
+ * // the result is:
+ * // [
+ * // 'x' => [
+ * // '123' => 'aaa',
+ * // '124' => 'bbb',
+ * // ],
+ * // 'y' => [
+ * // '345' => 'ccc',
+ * // ],
+ * // ]
+ * ~~~
+ *
+ * @param array $array
+ * @param string|\Closure $from
+ * @param string|\Closure $to
+ * @param string|\Closure $group
+ * @return array
+ */
+ public static function map($array, $from, $to, $group = null)
+ {
+ $result = [];
+ foreach ($array as $element) {
+ $key = static::getValue($element, $from);
+ $value = static::getValue($element, $to);
+ if ($group !== null) {
+ $result[static::getValue($element, $group)][$key] = $value;
+ } else {
+ $result[$key] = $value;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Checks if the given array contains the specified key.
+ * This method enhances the `array_key_exists()` function by supporting case-insensitive
+ * key comparison.
+ * @param string $key the key to check
+ * @param array $array the array with keys to check
+ * @param boolean $caseSensitive whether the key comparison should be case-sensitive
+ * @return boolean whether the array contains the specified key
+ */
+ public static function keyExists($key, $array, $caseSensitive = true)
+ {
+ if ($caseSensitive) {
+ return array_key_exists($key, $array);
+ } else {
+ foreach (array_keys($array) as $k) {
+ if (strcasecmp($key, $k) === 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Sorts an array of objects or arrays (with the same structure) by one or several keys.
+ * @param array $array the array to be sorted. The array will be modified after calling this method.
+ * @param string|\Closure|array $key the key(s) to be sorted by. This refers to a key name of the sub-array
+ * elements, a property name of the objects, or an anonymous function returning the values for comparison
+ * purpose. The anonymous function signature should be: `function($item)`.
+ * To sort by multiple keys, provide an array of keys here.
+ * @param integer|array $direction the sorting direction. It can be either `SORT_ASC` or `SORT_DESC`.
+ * When sorting by multiple keys with different sorting directions, use an array of sorting directions.
+ * @param integer|array $sortFlag the PHP sort flag. Valid values include
+ * `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING`, `SORT_LOCALE_STRING`, `SORT_NATURAL` and `SORT_FLAG_CASE`.
+ * Please refer to [PHP manual](http://php.net/manual/en/function.sort.php)
+ * for more details. When sorting by multiple keys with different sort flags, use an array of sort flags.
+ * @throws InvalidParamException if the $descending or $sortFlag parameters do not have
+ * correct number of elements as that of $key.
+ */
+ public static function multisort(&$array, $key, $direction = SORT_ASC, $sortFlag = SORT_REGULAR)
+ {
+ $keys = is_array($key) ? $key : [$key];
+ if (empty($keys) || empty($array)) {
+ return;
+ }
+ $n = count($keys);
+ if (is_scalar($direction)) {
+ $direction = array_fill(0, $n, $direction);
+ } elseif (count($direction) !== $n) {
+ throw new InvalidParamException('The length of $descending parameter must be the same as that of $keys.');
+ }
+ if (is_scalar($sortFlag)) {
+ $sortFlag = array_fill(0, $n, $sortFlag);
+ } elseif (count($sortFlag) !== $n) {
+ throw new InvalidParamException('The length of $sortFlag parameter must be the same as that of $keys.');
+ }
+ $args = [];
+ foreach ($keys as $i => $key) {
+ $flag = $sortFlag[$i];
+ $args[] = static::getColumn($array, $key);
+ $args[] = $direction[$i];
+ $args[] = $flag;
+ }
+ $args[] = &$array;
+ call_user_func_array('array_multisort', $args);
+ }
+
+ /**
+ * Encodes special characters in an array of strings into HTML entities.
+ * Both the array keys and values will be encoded.
+ * If a value is an array, this method will also encode it recursively.
+ * @param array $data data to be encoded
+ * @param boolean $valuesOnly whether to encode array values only. If false,
+ * both the array keys and array values will be encoded.
+ * @param string $charset the charset that the data is using. If not set,
+ * [[\yii\base\Application::charset]] will be used.
+ * @return array the encoded data
+ * @see http://www.php.net/manual/en/function.htmlspecialchars.php
+ */
+ public static function htmlEncode($data, $valuesOnly = true, $charset = null)
+ {
+ if ($charset === null) {
+ $charset = Yii::$app->charset;
+ }
+ $d = [];
+ foreach ($data as $key => $value) {
+ if (!$valuesOnly && is_string($key)) {
+ $key = htmlspecialchars($key, ENT_QUOTES, $charset);
+ }
+ if (is_string($value)) {
+ $d[$key] = htmlspecialchars($value, ENT_QUOTES, $charset);
+ } elseif (is_array($value)) {
+ $d[$key] = static::htmlEncode($value, $charset);
+ }
+ }
+ return $d;
+ }
+
+ /**
+ * Decodes HTML entities into the corresponding characters in an array of strings.
+ * Both the array keys and values will be decoded.
+ * If a value is an array, this method will also decode it recursively.
+ * @param array $data data to be decoded
+ * @param boolean $valuesOnly whether to decode array values only. If false,
+ * both the array keys and array values will be decoded.
+ * @return array the decoded data
+ * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
+ */
+ public static function htmlDecode($data, $valuesOnly = true)
+ {
+ $d = [];
+ foreach ($data as $key => $value) {
+ if (!$valuesOnly && is_string($key)) {
+ $key = htmlspecialchars_decode($key, ENT_QUOTES);
+ }
+ if (is_string($value)) {
+ $d[$key] = htmlspecialchars_decode($value, ENT_QUOTES);
+ } elseif (is_array($value)) {
+ $d[$key] = static::htmlDecode($value);
+ }
+ }
+ return $d;
+ }
+}
diff --git a/framework/yii/helpers/BaseConsole.php b/framework/yii/helpers/BaseConsole.php
new file mode 100644
index 0000000..c78ed67
--- /dev/null
+++ b/framework/yii/helpers/BaseConsole.php
@@ -0,0 +1,922 @@
+
+ * @since 2.0
+ */
+class BaseConsole
+{
+ const FG_BLACK = 30;
+ const FG_RED = 31;
+ const FG_GREEN = 32;
+ const FG_YELLOW = 33;
+ const FG_BLUE = 34;
+ const FG_PURPLE = 35;
+ const FG_CYAN = 36;
+ const FG_GREY = 37;
+
+ const BG_BLACK = 40;
+ const BG_RED = 41;
+ const BG_GREEN = 42;
+ const BG_YELLOW = 43;
+ const BG_BLUE = 44;
+ const BG_PURPLE = 45;
+ const BG_CYAN = 46;
+ const BG_GREY = 47;
+
+ const RESET = 0;
+ const NORMAL = 0;
+ const BOLD = 1;
+ const ITALIC = 3;
+ const UNDERLINE = 4;
+ const BLINK = 5;
+ const NEGATIVE = 7;
+ const CONCEALED = 8;
+ const CROSSED_OUT = 9;
+ const FRAMED = 51;
+ const ENCIRCLED = 52;
+ const OVERLINED = 53;
+
+ /**
+ * Moves the terminal cursor up by sending ANSI control code CUU to the terminal.
+ * If the cursor is already at the edge of the screen, this has no effect.
+ * @param integer $rows number of rows the cursor should be moved up
+ */
+ public static function moveCursorUp($rows = 1)
+ {
+ echo "\033[" . (int)$rows . 'A';
+ }
+
+ /**
+ * Moves the terminal cursor down by sending ANSI control code CUD to the terminal.
+ * If the cursor is already at the edge of the screen, this has no effect.
+ * @param integer $rows number of rows the cursor should be moved down
+ */
+ public static function moveCursorDown($rows = 1)
+ {
+ echo "\033[" . (int)$rows . 'B';
+ }
+
+ /**
+ * Moves the terminal cursor forward by sending ANSI control code CUF to the terminal.
+ * If the cursor is already at the edge of the screen, this has no effect.
+ * @param integer $steps number of steps the cursor should be moved forward
+ */
+ public static function moveCursorForward($steps = 1)
+ {
+ echo "\033[" . (int)$steps . 'C';
+ }
+
+ /**
+ * Moves the terminal cursor backward by sending ANSI control code CUB to the terminal.
+ * If the cursor is already at the edge of the screen, this has no effect.
+ * @param integer $steps number of steps the cursor should be moved backward
+ */
+ public static function moveCursorBackward($steps = 1)
+ {
+ echo "\033[" . (int)$steps . 'D';
+ }
+
+ /**
+ * Moves the terminal cursor to the beginning of the next line by sending ANSI control code CNL to the terminal.
+ * @param integer $lines number of lines the cursor should be moved down
+ */
+ public static function moveCursorNextLine($lines = 1)
+ {
+ echo "\033[" . (int)$lines . 'E';
+ }
+
+ /**
+ * Moves the terminal cursor to the beginning of the previous line by sending ANSI control code CPL to the terminal.
+ * @param integer $lines number of lines the cursor should be moved up
+ */
+ public static function moveCursorPrevLine($lines = 1)
+ {
+ echo "\033[" . (int)$lines . 'F';
+ }
+
+ /**
+ * Moves the cursor to an absolute position given as column and row by sending ANSI control code CUP or CHA to the terminal.
+ * @param integer $column 1-based column number, 1 is the left edge of the screen.
+ * @param integer|null $row 1-based row number, 1 is the top edge of the screen. if not set, will move cursor only in current line.
+ */
+ public static function moveCursorTo($column, $row = null)
+ {
+ if ($row === null) {
+ echo "\033[" . (int)$column . 'G';
+ } else {
+ echo "\033[" . (int)$row . ';' . (int)$column . 'H';
+ }
+ }
+
+ /**
+ * Scrolls whole page up by sending ANSI control code SU to the terminal.
+ * New lines are added at the bottom. This is not supported by ANSI.SYS used in windows.
+ * @param int $lines number of lines to scroll up
+ */
+ public static function scrollUp($lines = 1)
+ {
+ echo "\033[" . (int)$lines . "S";
+ }
+
+ /**
+ * Scrolls whole page down by sending ANSI control code SD to the terminal.
+ * New lines are added at the top. This is not supported by ANSI.SYS used in windows.
+ * @param int $lines number of lines to scroll down
+ */
+ public static function scrollDown($lines = 1)
+ {
+ echo "\033[" . (int)$lines . "T";
+ }
+
+ /**
+ * Saves the current cursor position by sending ANSI control code SCP to the terminal.
+ * Position can then be restored with [[restoreCursorPosition()]].
+ */
+ public static function saveCursorPosition()
+ {
+ echo "\033[s";
+ }
+
+ /**
+ * Restores the cursor position saved with [[saveCursorPosition()]] by sending ANSI control code RCP to the terminal.
+ */
+ public static function restoreCursorPosition()
+ {
+ echo "\033[u";
+ }
+
+ /**
+ * Hides the cursor by sending ANSI DECTCEM code ?25l to the terminal.
+ * Use [[showCursor()]] to bring it back.
+ * Do not forget to show cursor when your application exits. Cursor might stay hidden in terminal after exit.
+ */
+ public static function hideCursor()
+ {
+ echo "\033[?25l";
+ }
+
+ /**
+ * Will show a cursor again when it has been hidden by [[hideCursor()]] by sending ANSI DECTCEM code ?25h to the terminal.
+ */
+ public static function showCursor()
+ {
+ echo "\033[?25h";
+ }
+
+ /**
+ * Clears entire screen content by sending ANSI control code ED with argument 2 to the terminal.
+ * Cursor position will not be changed.
+ * **Note:** ANSI.SYS implementation used in windows will reset cursor position to upper left corner of the screen.
+ */
+ public static function clearScreen()
+ {
+ echo "\033[2J";
+ }
+
+ /**
+ * Clears text from cursor to the beginning of the screen by sending ANSI control code ED with argument 1 to the terminal.
+ * Cursor position will not be changed.
+ */
+ public static function clearScreenBeforeCursor()
+ {
+ echo "\033[1J";
+ }
+
+ /**
+ * Clears text from cursor to the end of the screen by sending ANSI control code ED with argument 0 to the terminal.
+ * Cursor position will not be changed.
+ */
+ public static function clearScreenAfterCursor()
+ {
+ echo "\033[0J";
+ }
+
+ /**
+ * Clears the line, the cursor is currently on by sending ANSI control code EL with argument 2 to the terminal.
+ * Cursor position will not be changed.
+ */
+ public static function clearLine()
+ {
+ echo "\033[2K";
+ }
+
+ /**
+ * Clears text from cursor position to the beginning of the line by sending ANSI control code EL with argument 1 to the terminal.
+ * Cursor position will not be changed.
+ */
+ public static function clearLineBeforeCursor()
+ {
+ echo "\033[1K";
+ }
+
+ /**
+ * Clears text from cursor position to the end of the line by sending ANSI control code EL with argument 0 to the terminal.
+ * Cursor position will not be changed.
+ */
+ public static function clearLineAfterCursor()
+ {
+ echo "\033[0K";
+ }
+
+ /**
+ * Returns the ANSI format code.
+ *
+ * @param array $format An array containing formatting values.
+ * You can pass any of the FG_*, BG_* and TEXT_* constants
+ * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format.
+ * @return string The ANSI format code according to the given formatting constants.
+ */
+ public static function ansiFormatCode($format)
+ {
+ return "\033[" . implode(';', $format) . 'm';
+ }
+
+ /**
+ * Echoes an ANSI format code that affects the formatting of any text that is printed afterwards.
+ *
+ * @param array $format An array containing formatting values.
+ * You can pass any of the FG_*, BG_* and TEXT_* constants
+ * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format.
+ * @see ansiFormatCode()
+ * @see ansiFormatEnd()
+ */
+ public static function beginAnsiFormat($format)
+ {
+ echo "\033[" . implode(';', $format) . 'm';
+ }
+
+ /**
+ * Resets any ANSI format set by previous method [[ansiFormatBegin()]]
+ * Any output after this will have default text format.
+ * This is equal to calling
+ *
+ * ```php
+ * echo Console::ansiFormatCode([Console::RESET])
+ * ```
+ */
+ public static function endAnsiFormat()
+ {
+ echo "\033[0m";
+ }
+
+ /**
+ * Will return a string formatted with the given ANSI style
+ *
+ * @param string $string the string to be formatted
+ * @param array $format An array containing formatting values.
+ * You can pass any of the FG_*, BG_* and TEXT_* constants
+ * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format.
+ * @return string
+ */
+ public static function ansiFormat($string, $format = [])
+ {
+ $code = implode(';', $format);
+ return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m";
+ }
+
+ /**
+ * Returns the ansi format code for xterm foreground color.
+ * You can pass the return value of this to one of the formatting methods:
+ * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]]
+ *
+ * @param integer $colorCode xterm color code
+ * @return string
+ * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors
+ */
+ public static function xtermFgColor($colorCode)
+ {
+ return '38;5;' . $colorCode;
+ }
+
+ /**
+ * Returns the ansi format code for xterm background color.
+ * You can pass the return value of this to one of the formatting methods:
+ * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]]
+ *
+ * @param integer $colorCode xterm color code
+ * @return string
+ * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors
+ */
+ public static function xtermBgColor($colorCode)
+ {
+ return '48;5;' . $colorCode;
+ }
+
+ /**
+ * Strips ANSI control codes from a string
+ *
+ * @param string $string String to strip
+ * @return string
+ */
+ public static function stripAnsiFormat($string)
+ {
+ return preg_replace('/\033\[[\d;?]*\w/', '', $string);
+ }
+
+ /**
+ * Converts an ANSI formatted string to HTML
+ * @param $string
+ * @return mixed
+ */
+ // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746
+ public static function ansiToHtml($string)
+ {
+ $tags = 0;
+ return preg_replace_callback(
+ '/\033\[[\d;]+m/',
+ function ($ansi) use (&$tags) {
+ $styleA = [];
+ foreach (explode(';', $ansi) as $controlCode) {
+ switch ($controlCode) {
+ case self::FG_BLACK:
+ $style = ['color' => '#000000'];
+ break;
+ case self::FG_BLUE:
+ $style = ['color' => '#000078'];
+ break;
+ case self::FG_CYAN:
+ $style = ['color' => '#007878'];
+ break;
+ case self::FG_GREEN:
+ $style = ['color' => '#007800'];
+ break;
+ case self::FG_GREY:
+ $style = ['color' => '#787878'];
+ break;
+ case self::FG_PURPLE:
+ $style = ['color' => '#780078'];
+ break;
+ case self::FG_RED:
+ $style = ['color' => '#780000'];
+ break;
+ case self::FG_YELLOW:
+ $style = ['color' => '#787800'];
+ break;
+ case self::BG_BLACK:
+ $style = ['background-color' => '#000000'];
+ break;
+ case self::BG_BLUE:
+ $style = ['background-color' => '#000078'];
+ break;
+ case self::BG_CYAN:
+ $style = ['background-color' => '#007878'];
+ break;
+ case self::BG_GREEN:
+ $style = ['background-color' => '#007800'];
+ break;
+ case self::BG_GREY:
+ $style = ['background-color' => '#787878'];
+ break;
+ case self::BG_PURPLE:
+ $style = ['background-color' => '#780078'];
+ break;
+ case self::BG_RED:
+ $style = ['background-color' => '#780000'];
+ break;
+ case self::BG_YELLOW:
+ $style = ['background-color' => '#787800'];
+ break;
+ case self::BOLD:
+ $style = ['font-weight' => 'bold'];
+ break;
+ case self::ITALIC:
+ $style = ['font-style' => 'italic'];
+ break;
+ case self::UNDERLINE:
+ $style = ['text-decoration' => ['underline']];
+ break;
+ case self::OVERLINED:
+ $style = ['text-decoration' => ['overline']];
+ break;
+ case self::CROSSED_OUT:
+ $style = ['text-decoration' => ['line-through']];
+ break;
+ case self::BLINK:
+ $style = ['text-decoration' => ['blink']];
+ break;
+ case self::NEGATIVE: // ???
+ case self::CONCEALED:
+ case self::ENCIRCLED:
+ case self::FRAMED:
+ // TODO allow resetting codes
+ break;
+ case 0: // ansi reset
+ $return = '';
+ for ($n = $tags; $tags > 0; $tags--) {
+ $return .= '';
+ }
+ return $return;
+ }
+
+ $styleA = ArrayHelper::merge($styleA, $style);
+ }
+ $styleString[] = [];
+ foreach ($styleA as $name => $content) {
+ if ($name === 'text-decoration') {
+ $content = implode(' ', $content);
+ }
+ $styleString[] = $name . ':' . $content;
+ }
+ $tags++;
+ return '';
+ },
+ $string
+ );
+ }
+
+ // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746
+ public function markdownToAnsi()
+ {
+ // TODO implement
+ }
+
+ /**
+ * Converts a string to ansi formatted by replacing patterns like %y (for yellow) with ansi control codes
+ *
+ * Uses almost the same syntax as https://github.com/pear/Console_Color2/blob/master/Console/Color2.php
+ * The conversion table is: ('bold' meaning 'light' on some
+ * terminals). It's almost the same conversion table irssi uses.
+ *
+ * text text background
+ * ------------------------------------------------
+ * %k %K %0 black dark grey black
+ * %r %R %1 red bold red red
+ * %g %G %2 green bold green green
+ * %y %Y %3 yellow bold yellow yellow
+ * %b %B %4 blue bold blue blue
+ * %m %M %5 magenta bold magenta magenta
+ * %p %P magenta (think: purple)
+ * %c %C %6 cyan bold cyan cyan
+ * %w %W %7 white bold white white
+ *
+ * %F Blinking, Flashing
+ * %U Underline
+ * %8 Reverse
+ * %_,%9 Bold
+ *
+ * %n Resets the color
+ * %% A single %
+ *
+ * First param is the string to convert, second is an optional flag if
+ * colors should be used. It defaults to true, if set to false, the
+ * colorcodes will just be removed (And %% will be transformed into %)
+ *
+ * @param string $string String to convert
+ * @param bool $colored Should the string be colored?
+ * @return string
+ */
+ // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746
+ public static function renderColoredString($string, $colored = true)
+ {
+ static $conversions = [
+ '%y' => [self::FG_YELLOW],
+ '%g' => [self::FG_GREEN],
+ '%b' => [self::FG_BLUE],
+ '%r' => [self::FG_RED],
+ '%p' => [self::FG_PURPLE],
+ '%m' => [self::FG_PURPLE],
+ '%c' => [self::FG_CYAN],
+ '%w' => [self::FG_GREY],
+ '%k' => [self::FG_BLACK],
+ '%n' => [0], // reset
+ '%Y' => [self::FG_YELLOW, self::BOLD],
+ '%G' => [self::FG_GREEN, self::BOLD],
+ '%B' => [self::FG_BLUE, self::BOLD],
+ '%R' => [self::FG_RED, self::BOLD],
+ '%P' => [self::FG_PURPLE, self::BOLD],
+ '%M' => [self::FG_PURPLE, self::BOLD],
+ '%C' => [self::FG_CYAN, self::BOLD],
+ '%W' => [self::FG_GREY, self::BOLD],
+ '%K' => [self::FG_BLACK, self::BOLD],
+ '%N' => [0, self::BOLD],
+ '%3' => [self::BG_YELLOW],
+ '%2' => [self::BG_GREEN],
+ '%4' => [self::BG_BLUE],
+ '%1' => [self::BG_RED],
+ '%5' => [self::BG_PURPLE],
+ '%6' => [self::BG_PURPLE],
+ '%7' => [self::BG_CYAN],
+ '%0' => [self::BG_GREY],
+ '%F' => [self::BLINK],
+ '%U' => [self::UNDERLINE],
+ '%8' => [self::NEGATIVE],
+ '%9' => [self::BOLD],
+ '%_' => [self::BOLD],
+ ];
+
+ if ($colored) {
+ $string = str_replace('%%', '% ', $string);
+ foreach ($conversions as $key => $value) {
+ $string = str_replace(
+ $key,
+ static::ansiFormatCode($value),
+ $string
+ );
+ }
+ $string = str_replace('% ', '%', $string);
+ } else {
+ $string = preg_replace('/%((%)|.)/', '$2', $string);
+ }
+ return $string;
+ }
+
+ /**
+ * Escapes % so they don't get interpreted as color codes when
+ * the string is parsed by [[renderColoredString]]
+ *
+ * @param string $string String to escape
+ *
+ * @access public
+ * @return string
+ */
+ // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746
+ public static function escape($string)
+ {
+ return str_replace('%', '%%', $string);
+ }
+
+ /**
+ * Returns true if the stream supports colorization. ANSI colors are disabled if not supported by the stream.
+ *
+ * - windows without ansicon
+ * - not tty consoles
+ *
+ * @param mixed $stream
+ * @return bool true if the stream supports ANSI colors, otherwise false.
+ */
+ public static function streamSupportsAnsiColors($stream)
+ {
+ return DIRECTORY_SEPARATOR == '\\'
+ ? getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON'
+ : function_exists('posix_isatty') && @posix_isatty($stream);
+ }
+
+ /**
+ * Returns true if the console is running on windows
+ * @return bool
+ */
+ public static function isRunningOnWindows()
+ {
+ return DIRECTORY_SEPARATOR == '\\';
+ }
+
+ /**
+ * Usage: list($width, $height) = ConsoleHelper::getScreenSize();
+ *
+ * @param bool $refresh whether to force checking and not re-use cached size value.
+ * This is useful to detect changing window size while the application is running but may
+ * not get up to date values on every terminal.
+ * @return array|boolean An array of ($width, $height) or false when it was not able to determine size.
+ */
+ public static function getScreenSize($refresh = false)
+ {
+ static $size;
+ if ($size !== null && !$refresh) {
+ return $size;
+ }
+
+ if (static::isRunningOnWindows()) {
+ $output = [];
+ exec('mode con', $output);
+ if (isset($output) && strpos($output[1], 'CON') !== false) {
+ return $size = [(int)preg_replace('~[^0-9]~', '', $output[3]), (int)preg_replace('~[^0-9]~', '', $output[4])];
+ }
+ } else {
+ // try stty if available
+ $stty = [];
+ if (exec('stty -a 2>&1', $stty) && preg_match('/rows\s+(\d+);\s*columns\s+(\d+);/mi', implode(' ', $stty), $matches)) {
+ return $size = [$matches[2], $matches[1]];
+ }
+
+ // fallback to tput, which may not be updated on terminal resize
+ if (($width = (int) exec('tput cols 2>&1')) > 0 && ($height = (int) exec('tput lines 2>&1')) > 0) {
+ return $size = [$width, $height];
+ }
+
+ // fallback to ENV variables, which may not be updated on terminal resize
+ if (($width = (int) getenv('COLUMNS')) > 0 && ($height = (int) getenv('LINES')) > 0) {
+ return $size = [$width, $height];
+ }
+ }
+
+ return $size = false;
+ }
+
+ /**
+ * Gets input from STDIN and returns a string right-trimmed for EOLs.
+ *
+ * @param bool $raw If set to true, returns the raw string without trimming
+ * @return string the string read from stdin
+ */
+ public static function stdin($raw = false)
+ {
+ return $raw ? fgets(STDIN) : rtrim(fgets(STDIN), PHP_EOL);
+ }
+
+ /**
+ * Prints a string to STDOUT.
+ *
+ * @param string $string the string to print
+ * @return int|boolean Number of bytes printed or false on error
+ */
+ public static function stdout($string)
+ {
+ return fwrite(STDOUT, $string);
+ }
+
+ /**
+ * Prints a string to STDERR.
+ *
+ * @param string $string the string to print
+ * @return int|boolean Number of bytes printed or false on error
+ */
+ public static function stderr($string)
+ {
+ return fwrite(STDERR, $string);
+ }
+
+ /**
+ * Asks the user for input. Ends when the user types a carriage return (PHP_EOL). Optionally, It also provides a
+ * prompt.
+ *
+ * @param string $prompt the prompt to display before waiting for input (optional)
+ * @return string the user's input
+ */
+ public static function input($prompt = null)
+ {
+ if (isset($prompt)) {
+ static::stdout($prompt);
+ }
+ return static::stdin();
+ }
+
+ /**
+ * Prints text to STDOUT appended with a carriage return (PHP_EOL).
+ *
+ * @param string $string the text to print
+ * @return integer|boolean number of bytes printed or false on error.
+ */
+ public static function output($string = null)
+ {
+ return static::stdout($string . PHP_EOL);
+ }
+
+ /**
+ * Prints text to STDERR appended with a carriage return (PHP_EOL).
+ *
+ * @param string $string the text to print
+ * @return integer|boolean number of bytes printed or false on error.
+ */
+ public static function error($string = null)
+ {
+ return static::stderr($string . PHP_EOL);
+ }
+
+ /**
+ * Prompts the user for input and validates it
+ *
+ * @param string $text prompt string
+ * @param array $options the options to validate the input:
+ * - required: whether it is required or not
+ * - default: default value if no input is inserted by the user
+ * - pattern: regular expression pattern to validate user input
+ * - validator: a callable function to validate input. The function must accept two parameters:
+ * - $input: the user input to validate
+ * - $error: the error value passed by reference if validation failed.
+ * @return string the user input
+ */
+ public static function prompt($text, $options = [])
+ {
+ $options = ArrayHelper::merge(
+ [
+ 'required' => false,
+ 'default' => null,
+ 'pattern' => null,
+ 'validator' => null,
+ 'error' => 'Invalid input.',
+ ],
+ $options
+ );
+ $error = null;
+
+ top:
+ $input = $options['default']
+ ? static::input("$text [" . $options['default'] . ']: ')
+ : static::input("$text: ");
+
+ if (!strlen($input)) {
+ if (isset($options['default'])) {
+ $input = $options['default'];
+ } elseif ($options['required']) {
+ static::output($options['error']);
+ goto top;
+ }
+ } elseif ($options['pattern'] && !preg_match($options['pattern'], $input)) {
+ static::output($options['error']);
+ goto top;
+ } elseif ($options['validator'] &&
+ !call_user_func_array($options['validator'], [$input, &$error])
+ ) {
+ static::output(isset($error) ? $error : $options['error']);
+ goto top;
+ }
+
+ return $input;
+ }
+
+ /**
+ * Asks user to confirm by typing y or n.
+ *
+ * @param string $message to echo out before waiting for user input
+ * @param boolean $default this value is returned if no selection is made.
+ * @return boolean whether user confirmed
+ */
+ public static function confirm($message, $default = true)
+ {
+ echo $message . ' (yes|no) [' . ($default ? 'yes' : 'no') . ']:';
+ $input = trim(static::stdin());
+ return empty($input) ? $default : !strncasecmp($input, 'y', 1);
+ }
+
+ /**
+ * Gives the user an option to choose from. Giving '?' as an input will show
+ * a list of options to choose from and their explanations.
+ *
+ * @param string $prompt the prompt message
+ * @param array $options Key-value array of options to choose from
+ *
+ * @return string An option character the user chose
+ */
+ public static function select($prompt, $options = [])
+ {
+ top:
+ static::stdout("$prompt [" . implode(',', array_keys($options)) . ",?]: ");
+ $input = static::stdin();
+ if ($input === '?') {
+ foreach ($options as $key => $value) {
+ static::output(" $key - $value");
+ }
+ static::output(" ? - Show help");
+ goto top;
+ } elseif (!in_array($input, array_keys($options))) {
+ goto top;
+ }
+ return $input;
+ }
+
+ private static $_progressStart;
+ private static $_progressWidth;
+ private static $_progressPrefix;
+
+ /**
+ * Starts display of a progress bar on screen.
+ *
+ * This bar will be updated by [[updateProgress()]] and my be ended by [[endProgress()]].
+ *
+ * The following example shows a simple usage of a progress bar:
+ *
+ * ```php
+ * Console::startProgress(0, 1000);
+ * for ($n = 1; $n <= 1000; $n++) {
+ * usleep(1000);
+ * Console::updateProgress($n, 1000);
+ * }
+ * Console::endProgress();
+ * ```
+ *
+ * Git clone like progress (showing only status information):
+ * ```php
+ * Console::startProgress(0, 1000, 'Counting objects: ', false);
+ * for ($n = 1; $n <= 1000; $n++) {
+ * usleep(1000);
+ * Console::updateProgress($n, 1000);
+ * }
+ * Console::endProgress("done." . PHP_EOL);
+ * ```
+ *
+ * @param integer $done the number of items that are completed.
+ * @param integer $total the total value of items that are to be done.
+ * @param string $prefix an optional string to display before the progress bar.
+ * Default to empty string which results in no prefix to be displayed.
+ * @param integer|boolean $width optional width of the progressbar. This can be an integer representing
+ * the number of characters to display for the progress bar or a float between 0 and 1 representing the
+ * percentage of screen with the progress bar may take. It can also be set to false to disable the
+ * bar and only show progress information like percent, number of items and ETA.
+ * If not set, the bar will be as wide as the screen. Screen size will be detected using [[getScreenSize()]].
+ * @see startProgress
+ * @see updateProgress
+ * @see endProgress
+ */
+ public static function startProgress($done, $total, $prefix = '', $width = null)
+ {
+ self::$_progressStart = time();
+ self::$_progressWidth = $width;
+ self::$_progressPrefix = $prefix;
+
+ static::updateProgress($done, $total);
+ }
+
+ /**
+ * Updates a progress bar that has been started by [[startProgress()]].
+ *
+ * @param integer $done the number of items that are completed.
+ * @param integer $total the total value of items that are to be done.
+ * @param string $prefix an optional string to display before the progress bar.
+ * Defaults to null meaning the prefix specified by [[startProgress()]] will be used.
+ * If prefix is specified it will update the prefix that will be used by later calls.
+ * @see startProgress
+ * @see endProgress
+ */
+ public static function updateProgress($done, $total, $prefix = null)
+ {
+ $width = self::$_progressWidth;
+ if ($width === false) {
+ $width = 0;
+ } else {
+ $screenSize = static::getScreenSize(true);
+ if ($screenSize === false && $width < 1) {
+ $width = 0;
+ } elseif ($width === null) {
+ $width = $screenSize[0];
+ } elseif ($width > 0 && $width < 1) {
+ $width = floor($screenSize[0] * $width);
+ }
+ }
+ if ($prefix === null) {
+ $prefix = self::$_progressPrefix;
+ } else {
+ self::$_progressPrefix = $prefix;
+ }
+ $width -= mb_strlen($prefix);
+
+ $percent = $done / $total;
+ $info = sprintf("%d%% (%d/%d)", $percent * 100, $done, $total);
+
+ if ($done > $total || $done == 0) {
+ $info .= ' ETA: n/a';
+ } elseif ($done < $total) {
+ $rate = (time() - self::$_progressStart) / $done;
+ $info .= sprintf(' ETA: %d sec.', $rate * ($total - $done));
+ }
+
+ $width -= 3 + mb_strlen($info);
+ // skipping progress bar on very small display or if forced to skip
+ if ($width < 5) {
+ static::stdout("\r$prefix$info ");
+ } else {
+ if ($percent < 0) {
+ $percent = 0;
+ } elseif ($percent > 1) {
+ $percent = 1;
+ }
+ $bar = floor($percent * $width);
+ $status = str_repeat("=", $bar);
+ if ($bar < $width) {
+ $status .= ">";
+ $status .= str_repeat(" ", $width - $bar - 1);
+ }
+ static::stdout("\r$prefix" . "[$status] $info");
+ }
+ flush();
+ }
+
+ /**
+ * Ends a progress bar that has been started by [[startProgress()]].
+ *
+ * @param string|boolean $remove This can be `false` to leave the progress bar on screen and just print a newline.
+ * If set to `true`, the line of the progress bar will be cleared. This may also be a string to be displayed instead
+ * of the progress bar.
+ * @param bool $keepPrefix whether to keep the prefix that has been specified for the progressbar when progressbar
+ * gets removed. Defaults to true.
+ * @see startProgress
+ * @see updateProgress
+ */
+ public static function endProgress($remove = false, $keepPrefix = true)
+ {
+ if ($remove === false) {
+ static::stdout(PHP_EOL);
+ } else {
+ if (static::streamSupportsAnsiColors(STDOUT)) {
+ static::clearLine();
+ }
+ static::stdout("\r" . ($keepPrefix ? self::$_progressPrefix : '') . (is_string($remove) ? $remove : ''));
+ }
+ flush();
+
+ self::$_progressStart = null;
+ self::$_progressWidth = null;
+ self::$_progressPrefix = '';
+ }
+}
diff --git a/framework/yii/helpers/BaseFileHelper.php b/framework/yii/helpers/BaseFileHelper.php
new file mode 100644
index 0000000..36591ae
--- /dev/null
+++ b/framework/yii/helpers/BaseFileHelper.php
@@ -0,0 +1,341 @@
+
+ * @author Alex Makarov
+ * @since 2.0
+ */
+class BaseFileHelper
+{
+ /**
+ * Normalizes a file/directory path.
+ * After normalization, the directory separators in the path will be `DIRECTORY_SEPARATOR`,
+ * and any trailing directory separators will be removed. For example, '/home\demo/' on Linux
+ * will be normalized as '/home/demo'.
+ * @param string $path the file/directory path to be normalized
+ * @param string $ds the directory separator to be used in the normalized result. Defaults to `DIRECTORY_SEPARATOR`.
+ * @return string the normalized file/directory path
+ */
+ public static function normalizePath($path, $ds = DIRECTORY_SEPARATOR)
+ {
+ return rtrim(strtr($path, ['/' => $ds, '\\' => $ds]), $ds);
+ }
+
+ /**
+ * Returns the localized version of a specified file.
+ *
+ * The searching is based on the specified language code. In particular,
+ * a file with the same name will be looked for under the subdirectory
+ * whose name is the same as the language code. For example, given the file "path/to/view.php"
+ * and language code "zh_CN", the localized file will be looked for as
+ * "path/to/zh_CN/view.php". If the file is not found, the original file
+ * will be returned.
+ *
+ * If the target and the source language codes are the same,
+ * the original file will be returned.
+ *
+ * @param string $file the original file
+ * @param string $language the target language that the file should be localized to.
+ * If not set, the value of [[\yii\base\Application::language]] will be used.
+ * @param string $sourceLanguage the language that the original file is in.
+ * If not set, the value of [[\yii\base\Application::sourceLanguage]] will be used.
+ * @return string the matching localized file, or the original file if the localized version is not found.
+ * If the target and the source language codes are the same, the original file will be returned.
+ */
+ public static function localize($file, $language = null, $sourceLanguage = null)
+ {
+ if ($language === null) {
+ $language = Yii::$app->language;
+ }
+ if ($sourceLanguage === null) {
+ $sourceLanguage = Yii::$app->sourceLanguage;
+ }
+ if ($language === $sourceLanguage) {
+ return $file;
+ }
+ $desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . basename($file);
+ return is_file($desiredFile) ? $desiredFile : $file;
+ }
+
+ /**
+ * Determines the MIME type of the specified file.
+ * This method will first try to determine the MIME type based on
+ * [finfo_open](http://php.net/manual/en/function.finfo-open.php). If this doesn't work, it will
+ * fall back to [[getMimeTypeByExtension()]].
+ * @param string $file the file name.
+ * @param string $magicFile name of the optional magic database file, usually something like `/path/to/magic.mime`.
+ * This will be passed as the second parameter to [finfo_open](http://php.net/manual/en/function.finfo-open.php).
+ * @param boolean $checkExtension whether to use the file extension to determine the MIME type in case
+ * `finfo_open()` cannot determine it.
+ * @return string the MIME type (e.g. `text/plain`). Null is returned if the MIME type cannot be determined.
+ */
+ public static function getMimeType($file, $magicFile = null, $checkExtension = true)
+ {
+ if (function_exists('finfo_open')) {
+ $info = finfo_open(FILEINFO_MIME_TYPE, $magicFile);
+ if ($info) {
+ $result = finfo_file($info, $file);
+ finfo_close($info);
+ if ($result !== false) {
+ return $result;
+ }
+ }
+ }
+
+ return $checkExtension ? static::getMimeTypeByExtension($file) : null;
+ }
+
+ /**
+ * Determines the MIME type based on the extension name of the specified file.
+ * This method will use a local map between extension names and MIME types.
+ * @param string $file the file name.
+ * @param string $magicFile the path of the file that contains all available MIME type information.
+ * If this is not set, the default file aliased by `@yii/util/mimeTypes.php` will be used.
+ * @return string the MIME type. Null is returned if the MIME type cannot be determined.
+ */
+ public static function getMimeTypeByExtension($file, $magicFile = null)
+ {
+ static $mimeTypes = [];
+ if ($magicFile === null) {
+ $magicFile = __DIR__ . '/mimeTypes.php';
+ }
+ if (!isset($mimeTypes[$magicFile])) {
+ $mimeTypes[$magicFile] = require($magicFile);
+ }
+ if (($ext = pathinfo($file, PATHINFO_EXTENSION)) !== '') {
+ $ext = strtolower($ext);
+ if (isset($mimeTypes[$magicFile][$ext])) {
+ return $mimeTypes[$magicFile][$ext];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Copies a whole directory as another one.
+ * The files and sub-directories will also be copied over.
+ * @param string $src the source directory
+ * @param string $dst the destination directory
+ * @param array $options options for directory copy. Valid options are:
+ *
+ * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0775.
+ * - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting.
+ * - filter: callback, a PHP callback that is called for each directory or file.
+ * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered.
+ * The callback can return one of the following values:
+ *
+ * * true: the directory or file will be copied (the "only" and "except" options will be ignored)
+ * * false: the directory or file will NOT be copied (the "only" and "except" options will be ignored)
+ * * null: the "only" and "except" options will determine whether the directory or file should be copied
+ *
+ * - only: array, list of patterns that the file paths should match if they want to be copied.
+ * A path matches a pattern if it contains the pattern string at its end.
+ * For example, '.php' matches all file paths ending with '.php'.
+ * Note, the '/' characters in a pattern matches both '/' and '\' in the paths.
+ * If a file path matches a pattern in both "only" and "except", it will NOT be copied.
+ * - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied.
+ * A path matches a pattern if it contains the pattern string at its end.
+ * Patterns ending with '/' apply to directory paths only, and patterns not ending with '/'
+ * apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b';
+ * and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches
+ * both '/' and '\' in the paths.
+ * - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true.
+ * - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.
+ * If the callback returns false, the copy operation for the sub-directory or file will be cancelled.
+ * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or
+ * file to be copied from, while `$to` is the copy target.
+ * - afterCopy: callback, a PHP callback that is called after each sub-directory or file is successfully copied.
+ * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or
+ * file copied from, while `$to` is the copy target.
+ */
+ public static function copyDirectory($src, $dst, $options = [])
+ {
+ if (!is_dir($dst)) {
+ static::createDirectory($dst, isset($options['dirMode']) ? $options['dirMode'] : 0775, true);
+ }
+
+ $handle = opendir($src);
+ while (($file = readdir($handle)) !== false) {
+ if ($file === '.' || $file === '..') {
+ continue;
+ }
+ $from = $src . DIRECTORY_SEPARATOR . $file;
+ $to = $dst . DIRECTORY_SEPARATOR . $file;
+ if (static::filterPath($from, $options)) {
+ if (isset($options['beforeCopy']) && !call_user_func($options['beforeCopy'], $from, $to)) {
+ continue;
+ }
+ if (is_file($from)) {
+ copy($from, $to);
+ if (isset($options['fileMode'])) {
+ @chmod($to, $options['fileMode']);
+ }
+ } else {
+ static::copyDirectory($from, $to, $options);
+ }
+ if (isset($options['afterCopy'])) {
+ call_user_func($options['afterCopy'], $from, $to);
+ }
+ }
+ }
+ closedir($handle);
+ }
+
+ /**
+ * Removes a directory (and all its content) recursively.
+ * @param string $dir the directory to be deleted recursively.
+ */
+ public static function removeDirectory($dir)
+ {
+ if (!is_dir($dir) || !($handle = opendir($dir))) {
+ return;
+ }
+ while (($file = readdir($handle)) !== false) {
+ if ($file === '.' || $file === '..') {
+ continue;
+ }
+ $path = $dir . DIRECTORY_SEPARATOR . $file;
+ if (is_file($path)) {
+ unlink($path);
+ } else {
+ static::removeDirectory($path);
+ }
+ }
+ closedir($handle);
+ rmdir($dir);
+ }
+
+ /**
+ * Returns the files found under the specified directory and subdirectories.
+ * @param string $dir the directory under which the files will be looked for.
+ * @param array $options options for file searching. Valid options are:
+ *
+ * - filter: callback, a PHP callback that is called for each directory or file.
+ * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered.
+ * The callback can return one of the following values:
+ *
+ * * true: the directory or file will be returned (the "only" and "except" options will be ignored)
+ * * false: the directory or file will NOT be returned (the "only" and "except" options will be ignored)
+ * * null: the "only" and "except" options will determine whether the directory or file should be returned
+ *
+ * - only: array, list of patterns that the file paths should match if they want to be returned.
+ * A path matches a pattern if it contains the pattern string at its end.
+ * For example, '.php' matches all file paths ending with '.php'.
+ * Note, the '/' characters in a pattern matches both '/' and '\' in the paths.
+ * If a file path matches a pattern in both "only" and "except", it will NOT be returned.
+ * - except: array, list of patterns that the file paths or directory paths should match if they want to be excluded from the result.
+ * A path matches a pattern if it contains the pattern string at its end.
+ * Patterns ending with '/' apply to directory paths only, and patterns not ending with '/'
+ * apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b';
+ * and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches
+ * both '/' and '\' in the paths.
+ * - recursive: boolean, whether the files under the subdirectories should also be looked for. Defaults to true.
+ * @return array files found under the directory. The file list is sorted.
+ */
+ public static function findFiles($dir, $options = [])
+ {
+ $list = [];
+ $handle = opendir($dir);
+ while (($file = readdir($handle)) !== false) {
+ if ($file === '.' || $file === '..') {
+ continue;
+ }
+ $path = $dir . DIRECTORY_SEPARATOR . $file;
+ if (static::filterPath($path, $options)) {
+ if (is_file($path)) {
+ $list[] = $path;
+ } elseif (!isset($options['recursive']) || $options['recursive']) {
+ $list = array_merge($list, static::findFiles($path, $options));
+ }
+ }
+ }
+ closedir($handle);
+ return $list;
+ }
+
+ /**
+ * Checks if the given file path satisfies the filtering options.
+ * @param string $path the path of the file or directory to be checked
+ * @param array $options the filtering options. See [[findFiles()]] for explanations of
+ * the supported options.
+ * @return boolean whether the file or directory satisfies the filtering options.
+ */
+ public static function filterPath($path, $options)
+ {
+ if (isset($options['filter'])) {
+ $result = call_user_func($options['filter'], $path);
+ if (is_bool($result)) {
+ return $result;
+ }
+ }
+
+ if (empty($options['except']) && empty($options['only'])) {
+ return true;
+ }
+
+ $path = str_replace('\\', '/', $path);
+ if ($isDir = is_dir($path)) {
+ $path .= '/';
+ }
+ $n = StringHelper::byteLength($path);
+
+ if (!empty($options['except'])) {
+ foreach ($options['except'] as $name) {
+ if (StringHelper::byteSubstr($path, -StringHelper::byteLength($name), $n) === $name) {
+ return false;
+ }
+ }
+ }
+
+ if (!$isDir && !empty($options['only'])) {
+ foreach ($options['only'] as $name) {
+ if (StringHelper::byteSubstr($path, -StringHelper::byteLength($name), $n) === $name) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Creates a new directory.
+ *
+ * This method is similar to the PHP `mkdir()` function except that
+ * it uses `chmod()` to set the permission of the created directory
+ * in order to avoid the impact of the `umask` setting.
+ *
+ * @param string $path path of the directory to be created.
+ * @param integer $mode the permission to be set for the created directory.
+ * @param boolean $recursive whether to create parent directories if they do not exist.
+ * @return boolean whether the directory is created successfully
+ */
+ public static function createDirectory($path, $mode = 0775, $recursive = true)
+ {
+ if (is_dir($path)) {
+ return true;
+ }
+ $parentDir = dirname($path);
+ if ($recursive && !is_dir($parentDir)) {
+ static::createDirectory($parentDir, $mode, true);
+ }
+ $result = mkdir($path, $mode);
+ chmod($path, $mode);
+ return $result;
+ }
+}
diff --git a/framework/yii/helpers/BaseHtml.php b/framework/yii/helpers/BaseHtml.php
new file mode 100644
index 0000000..49fe832
--- /dev/null
+++ b/framework/yii/helpers/BaseHtml.php
@@ -0,0 +1,1640 @@
+
+ * @since 2.0
+ */
+class BaseHtml
+{
+ /**
+ * @var array list of void elements (element name => 1)
+ * @see http://www.w3.org/TR/html-markup/syntax.html#void-element
+ */
+ public static $voidElements = [
+ 'area' => 1,
+ 'base' => 1,
+ 'br' => 1,
+ 'col' => 1,
+ 'command' => 1,
+ 'embed' => 1,
+ 'hr' => 1,
+ 'img' => 1,
+ 'input' => 1,
+ 'keygen' => 1,
+ 'link' => 1,
+ 'meta' => 1,
+ 'param' => 1,
+ 'source' => 1,
+ 'track' => 1,
+ 'wbr' => 1,
+ ];
+ /**
+ * @var array the preferred order of attributes in a tag. This mainly affects the order of the attributes
+ * that are rendered by [[renderTagAttributes()]].
+ */
+ public static $attributeOrder = [
+ 'type',
+ 'id',
+ 'class',
+ 'name',
+ 'value',
+
+ 'href',
+ 'src',
+ 'action',
+ 'method',
+
+ 'selected',
+ 'checked',
+ 'readonly',
+ 'disabled',
+ 'multiple',
+
+ 'size',
+ 'maxlength',
+ 'width',
+ 'height',
+ 'rows',
+ 'cols',
+
+ 'alt',
+ 'title',
+ 'rel',
+ 'media',
+ ];
+
+ /**
+ * Encodes special characters into HTML entities.
+ * The [[yii\base\Application::charset|application charset]] will be used for encoding.
+ * @param string $content the content to be encoded
+ * @param boolean $doubleEncode whether to encode HTML entities in `$content`. If false,
+ * HTML entities in `$content` will not be further encoded.
+ * @return string the encoded content
+ * @see decode()
+ * @see http://www.php.net/manual/en/function.htmlspecialchars.php
+ */
+ public static function encode($content, $doubleEncode = true)
+ {
+ return htmlspecialchars($content, ENT_QUOTES, Yii::$app->charset, $doubleEncode);
+ }
+
+ /**
+ * Decodes special HTML entities back to the corresponding characters.
+ * This is the opposite of [[encode()]].
+ * @param string $content the content to be decoded
+ * @return string the decoded content
+ * @see encode()
+ * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
+ */
+ public static function decode($content)
+ {
+ return htmlspecialchars_decode($content, ENT_QUOTES);
+ }
+
+ /**
+ * Generates a complete HTML tag.
+ * @param string $name the tag name
+ * @param string $content the content to be enclosed between the start and end tags. It will not be HTML-encoded.
+ * If this is coming from end users, you should consider [[encode()]] it to prevent XSS attacks.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated HTML tag
+ * @see beginTag()
+ * @see endTag()
+ */
+ public static function tag($name, $content = '', $options = [])
+ {
+ $html = "<$name" . static::renderTagAttributes($options) . '>';
+ return isset(static::$voidElements[strtolower($name)]) ? $html : "$html$content$name>";
+ }
+
+ /**
+ * Generates a start tag.
+ * @param string $name the tag name
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated start tag
+ * @see endTag()
+ * @see tag()
+ */
+ public static function beginTag($name, $options = [])
+ {
+ return "<$name" . static::renderTagAttributes($options) . '>';
+ }
+
+ /**
+ * Generates an end tag.
+ * @param string $name the tag name
+ * @return string the generated end tag
+ * @see beginTag()
+ * @see tag()
+ */
+ public static function endTag($name)
+ {
+ return "$name>";
+ }
+
+ /**
+ * Generates a style tag.
+ * @param string $content the style content
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * If the options does not contain "type", a "type" attribute with value "text/css" will be used.
+ * @return string the generated style tag
+ */
+ public static function style($content, $options = [])
+ {
+ return static::tag('style', $content, $options);
+ }
+
+ /**
+ * Generates a script tag.
+ * @param string $content the script content
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * If the options does not contain "type", a "type" attribute with value "text/javascript" will be rendered.
+ * @return string the generated script tag
+ */
+ public static function script($content, $options = [])
+ {
+ return static::tag('script', $content, $options);
+ }
+
+ /**
+ * Generates a link tag that refers to an external CSS file.
+ * @param array|string $url the URL of the external CSS file. This parameter will be processed by [[url()]].
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated link tag
+ * @see url()
+ */
+ public static function cssFile($url, $options = [])
+ {
+ if (!isset($options['rel'])) {
+ $options['rel'] = 'stylesheet';
+ }
+ $options['href'] = static::url($url);
+ return static::tag('link', '', $options);
+ }
+
+ /**
+ * Generates a script tag that refers to an external JavaScript file.
+ * @param string $url the URL of the external JavaScript file. This parameter will be processed by [[url()]].
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated script tag
+ * @see url()
+ */
+ public static function jsFile($url, $options = [])
+ {
+ $options['src'] = static::url($url);
+ return static::tag('script', '', $options);
+ }
+
+ /**
+ * Generates a form start tag.
+ * @param array|string $action the form action URL. This parameter will be processed by [[url()]].
+ * @param string $method the form submission method, such as "post", "get", "put", "delete" (case-insensitive).
+ * Since most browsers only support "post" and "get", if other methods are given, they will
+ * be simulated using "post", and a hidden input will be added which contains the actual method type.
+ * See [[\yii\web\Request::restVar]] for more details.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated form start tag.
+ * @see endForm()
+ */
+ public static function beginForm($action = '', $method = 'post', $options = [])
+ {
+ $action = static::url($action);
+
+ $hiddenInputs = [];
+
+ $request = Yii::$app->getRequest();
+ if ($request instanceof Request) {
+ if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) {
+ // simulate PUT, DELETE, etc. via POST
+ $hiddenInputs[] = static::hiddenInput($request->restVar, $method);
+ $method = 'post';
+ }
+ if ($request->enableCsrfValidation && !strcasecmp($method, 'post')) {
+ $hiddenInputs[] = static::hiddenInput($request->csrfVar, $request->getCsrfToken());
+ }
+ }
+
+ if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) {
+ // query parameters in the action are ignored for GET method
+ // we use hidden fields to add them back
+ foreach (explode('&', substr($action, $pos + 1)) as $pair) {
+ if (($pos1 = strpos($pair, '=')) !== false) {
+ $hiddenInputs[] = static::hiddenInput(
+ urldecode(substr($pair, 0, $pos1)),
+ urldecode(substr($pair, $pos1 + 1))
+ );
+ } else {
+ $hiddenInputs[] = static::hiddenInput(urldecode($pair), '');
+ }
+ }
+ $action = substr($action, 0, $pos);
+ }
+
+ $options['action'] = $action;
+ $options['method'] = $method;
+ $form = static::beginTag('form', $options);
+ if (!empty($hiddenInputs)) {
+ $form .= "\n" . implode("\n", $hiddenInputs);
+ }
+
+ return $form;
+ }
+
+ /**
+ * Generates a form end tag.
+ * @return string the generated tag
+ * @see beginForm()
+ */
+ public static function endForm()
+ {
+ return '';
+ }
+
+ /**
+ * Generates a hyperlink tag.
+ * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
+ * such as an image tag. If this is coming from end users, you should consider [[encode()]]
+ * it to prevent XSS attacks.
+ * @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[url()]]
+ * and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute
+ * will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated hyperlink
+ * @see url()
+ */
+ public static function a($text, $url = null, $options = [])
+ {
+ if ($url !== null) {
+ $options['href'] = static::url($url);
+ }
+ return static::tag('a', $text, $options);
+ }
+
+ /**
+ * Generates a mailto hyperlink.
+ * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
+ * such as an image tag. If this is coming from end users, you should consider [[encode()]]
+ * it to prevent XSS attacks.
+ * @param string $email email address. If this is null, the first parameter (link body) will be treated
+ * as the email address and used.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated mailto link
+ */
+ public static function mailto($text, $email = null, $options = [])
+ {
+ $options['href'] = 'mailto:' . ($email === null ? $text : $email);
+ return static::tag('a', $text, $options);
+ }
+
+ /**
+ * Generates an image tag.
+ * @param string $src the image URL. This parameter will be processed by [[url()]].
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated image tag
+ */
+ public static function img($src, $options = [])
+ {
+ $options['src'] = static::url($src);
+ if (!isset($options['alt'])) {
+ $options['alt'] = '';
+ }
+ return static::tag('img', '', $options);
+ }
+
+ /**
+ * Generates a label tag.
+ * @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code
+ * such as an image tag. If this is is coming from end users, you should [[encode()]]
+ * it to prevent XSS attacks.
+ * @param string $for the ID of the HTML element that this label is associated with.
+ * If this is null, the "for" attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated label tag
+ */
+ public static function label($content, $for = null, $options = [])
+ {
+ $options['for'] = $for;
+ return static::tag('label', $content, $options);
+ }
+
+ /**
+ * Generates a button tag.
+ * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
+ * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
+ * you should consider [[encode()]] it to prevent XSS attacks.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function button($content = 'Button', $options = [])
+ {
+ return static::tag('button', $content, $options);
+ }
+
+ /**
+ * Generates a submit button tag.
+ * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
+ * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
+ * you should consider [[encode()]] it to prevent XSS attacks.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated submit button tag
+ */
+ public static function submitButton($content = 'Submit', $options = [])
+ {
+ $options['type'] = 'submit';
+ return static::button($content, $options);
+ }
+
+ /**
+ * Generates a reset button tag.
+ * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
+ * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
+ * you should consider [[encode()]] it to prevent XSS attacks.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated reset button tag
+ */
+ public static function resetButton($content = 'Reset', $options = [])
+ {
+ $options['type'] = 'reset';
+ return static::button($content, $options);
+ }
+
+ /**
+ * Generates an input type of the given type.
+ * @param string $type the type attribute.
+ * @param string $name the name attribute. If it is null, the name attribute will not be generated.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated input tag
+ */
+ public static function input($type, $name = null, $value = null, $options = [])
+ {
+ $options['type'] = $type;
+ $options['name'] = $name;
+ $options['value'] = $value === null ? null : (string)$value;
+ return static::tag('input', '', $options);
+ }
+
+ /**
+ * Generates an input button.
+ * @param string $label the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function buttonInput($label = 'Button', $options = [])
+ {
+ $options['type'] = 'button';
+ $options['value'] = $label;
+ return static::tag('input', '', $options);
+ }
+
+ /**
+ * Generates a submit input button.
+ * @param string $label the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function submitInput($label = 'Submit', $options = [])
+ {
+ $options['type'] = 'submit';
+ $options['value'] = $label;
+ return static::tag('input', '', $options);
+ }
+
+ /**
+ * Generates a reset input button.
+ * @param string $label the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]].
+ * Attributes whose value is null will be ignored and not put in the tag returned.
+ * @return string the generated button tag
+ */
+ public static function resetInput($label = 'Reset', $options = [])
+ {
+ $options['type'] = 'reset';
+ $options['value'] = $label;
+ return static::tag('input', '', $options);
+ }
+
+ /**
+ * Generates a text input field.
+ * @param string $name the name attribute.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function textInput($name, $value = null, $options = [])
+ {
+ return static::input('text', $name, $value, $options);
+ }
+
+ /**
+ * Generates a hidden input field.
+ * @param string $name the name attribute.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function hiddenInput($name, $value = null, $options = [])
+ {
+ return static::input('hidden', $name, $value, $options);
+ }
+
+ /**
+ * Generates a password input field.
+ * @param string $name the name attribute.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function passwordInput($name, $value = null, $options = [])
+ {
+ return static::input('password', $name, $value, $options);
+ }
+
+ /**
+ * Generates a file input field.
+ * To use a file input field, you should set the enclosing form's "enctype" attribute to
+ * be "multipart/form-data". After the form is submitted, the uploaded file information
+ * can be obtained via $_FILES[$name] (see PHP documentation).
+ * @param string $name the name attribute.
+ * @param string $value the value attribute. If it is null, the value attribute will not be generated.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated button tag
+ */
+ public static function fileInput($name, $value = null, $options = [])
+ {
+ return static::input('file', $name, $value, $options);
+ }
+
+ /**
+ * Generates a text area input.
+ * @param string $name the input name
+ * @param string $value the input value. Note that it will be encoded using [[encode()]].
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * @return string the generated text area tag
+ */
+ public static function textarea($name, $value = '', $options = [])
+ {
+ $options['name'] = $name;
+ return static::tag('textarea', static::encode($value), $options);
+ }
+
+ /**
+ * Generates a radio button input.
+ * @param string $name the name attribute.
+ * @param boolean $checked whether the radio button should be checked.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - uncheck: string, the value associated with the uncheck state of the radio button. When this attribute
+ * is present, a hidden input will be generated so that if the radio button is not checked and is submitted,
+ * the value of this attribute will still be submitted to the server via the hidden input.
+ * - label: string, a label displayed next to the radio button. It will NOT be HTML-encoded. Therefore you can pass
+ * in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
+ * When this option is specified, the radio button will be enclosed by a label tag.
+ * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
+ * - container: array|boolean, the HTML attributes for the container tag. This is only used when the "label" option is specified.
+ * If it is false, no container will be rendered. If it is an array or not, a "div" container will be rendered
+ * around the the radio button.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting radio button tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated radio button tag
+ */
+ public static function radio($name, $checked = false, $options = [])
+ {
+ $options['checked'] = (boolean)$checked;
+ $value = array_key_exists('value', $options) ? $options['value'] : '1';
+ if (isset($options['uncheck'])) {
+ // add a hidden field so that if the radio button is not selected, it still submits a value
+ $hidden = static::hiddenInput($name, $options['uncheck']);
+ unset($options['uncheck']);
+ } else {
+ $hidden = '';
+ }
+ if (isset($options['label'])) {
+ $label = $options['label'];
+ $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];
+ $container = isset($options['container']) ? $options['container'] : ['class' => 'radio'];
+ unset($options['label'], $options['labelOptions'], $options['container']);
+ $content = static::label(static::input('radio', $name, $value, $options) . ' ' . $label, null, $labelOptions);
+ if (is_array($container)) {
+ return $hidden . static::tag('div', $content, $container);
+ } else {
+ return $hidden . $content;
+ }
+ } else {
+ return $hidden . static::input('radio', $name, $value, $options);
+ }
+ }
+
+ /**
+ * Generates a checkbox input.
+ * @param string $name the name attribute.
+ * @param boolean $checked whether the checkbox should be checked.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute
+ * is present, a hidden input will be generated so that if the checkbox is not checked and is submitted,
+ * the value of this attribute will still be submitted to the server via the hidden input.
+ * - label: string, a label displayed next to the checkbox. It will NOT be HTML-encoded. Therefore you can pass
+ * in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
+ * When this option is specified, the checkbox will be enclosed by a label tag.
+ * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
+ * - container: array|boolean, the HTML attributes for the container tag. This is only used when the "label" option is specified.
+ * If it is false, no container will be rendered. If it is an array or not, a "div" container will be rendered
+ * around the the radio button.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting checkbox tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated checkbox tag
+ */
+ public static function checkbox($name, $checked = false, $options = [])
+ {
+ $options['checked'] = (boolean)$checked;
+ $value = array_key_exists('value', $options) ? $options['value'] : '1';
+ if (isset($options['uncheck'])) {
+ // add a hidden field so that if the checkbox is not selected, it still submits a value
+ $hidden = static::hiddenInput($name, $options['uncheck']);
+ unset($options['uncheck']);
+ } else {
+ $hidden = '';
+ }
+ if (isset($options['label'])) {
+ $label = $options['label'];
+ $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];
+ $container = isset($options['container']) ? $options['container'] : ['class' => 'checkbox'];
+ unset($options['label'], $options['labelOptions'], $options['container']);
+ $content = static::label(static::input('checkbox', $name, $value, $options) . ' ' . $label, null, $labelOptions);
+ if (is_array($container)) {
+ return $hidden . static::tag('div', $content, $container);
+ } else {
+ return $hidden . $content;
+ }
+ } else {
+ return $hidden . static::input('checkbox', $name, $value, $options);
+ }
+ }
+
+ /**
+ * Generates a drop-down list.
+ * @param string $name the input name
+ * @param string $selection the selected value
+ * @param array $items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * [[\yii\helpers\ArrayHelper::map()]].
+ *
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - prompt: string, a prompt text to be displayed as the first option;
+ * - options: array, the attributes for the select option tags. The array keys must be valid option values,
+ * and the array values are the extra attributes for the corresponding option tags. For example,
+ *
+ * ~~~
+ * [
+ * 'value1' => ['disabled' => true],
+ * 'value2' => ['label' => 'value 2'],
+ * ];
+ * ~~~
+ *
+ * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
+ * except that the array keys represent the optgroup labels specified in $items.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated drop-down list tag
+ */
+ public static function dropDownList($name, $selection = null, $items = [], $options = [])
+ {
+ if (!empty($options['multiple'])) {
+ return static::listBox($name, $selection, $items, $options);
+ }
+ $options['name'] = $name;
+ $selectOptions = static::renderSelectOptions($selection, $items, $options);
+ return static::tag('select', "\n" . $selectOptions . "\n", $options);
+ }
+
+ /**
+ * Generates a list box.
+ * @param string $name the input name
+ * @param string|array $selection the selected value(s)
+ * @param array $items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * [[\yii\helpers\ArrayHelper::map()]].
+ *
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - prompt: string, a prompt text to be displayed as the first option;
+ * - options: array, the attributes for the select option tags. The array keys must be valid option values,
+ * and the array values are the extra attributes for the corresponding option tags. For example,
+ *
+ * ~~~
+ * [
+ * 'value1' => ['disabled' => true],
+ * 'value2' => ['label' => 'value 2'],
+ * ];
+ * ~~~
+ *
+ * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
+ * except that the array keys represent the optgroup labels specified in $items.
+ * - unselect: string, the value that will be submitted when no option is selected.
+ * When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
+ * mode, we can still obtain the posted unselect value.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated list box tag
+ */
+ public static function listBox($name, $selection = null, $items = [], $options = [])
+ {
+ if (!isset($options['size'])) {
+ $options['size'] = 4;
+ }
+ if (!empty($options['multiple']) && substr($name, -2) !== '[]') {
+ $name .= '[]';
+ }
+ $options['name'] = $name;
+ if (isset($options['unselect'])) {
+ // add a hidden field so that if the list box has no option being selected, it still submits a value
+ if (substr($name, -2) === '[]') {
+ $name = substr($name, 0, -2);
+ }
+ $hidden = static::hiddenInput($name, $options['unselect']);
+ unset($options['unselect']);
+ } else {
+ $hidden = '';
+ }
+ $selectOptions = static::renderSelectOptions($selection, $items, $options);
+ return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options);
+ }
+
+ /**
+ * Generates a list of checkboxes.
+ * A checkbox list allows multiple selection, like [[listBox()]].
+ * As a result, the corresponding submitted value is an array.
+ * @param string $name the name attribute of each checkbox.
+ * @param string|array $selection the selected value(s).
+ * @param array $items the data item used to generate the checkboxes.
+ * The array values are the labels, while the array keys are the corresponding checkbox values.
+ * @param array $options options (name => config) for the checkbox list container tag.
+ * The following options are specially handled:
+ *
+ * - tag: string, the tag name of the container element.
+ * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
+ * By setting this option, a hidden input will be generated.
+ * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
+ * This option is ignored if `item` option is set.
+ * - separator: string, the HTML code that separates items.
+ * - itemOptions: array, the options for generating the radio button tag using [[checkbox()]].
+ * - item: callable, a callback that can be used to customize the generation of the HTML code
+ * corresponding to a single item in $items. The signature of this callback must be:
+ *
+ * ~~~
+ * function ($index, $label, $name, $checked, $value)
+ * ~~~
+ *
+ * where $index is the zero-based index of the checkbox in the whole list; $label
+ * is the label for the checkbox; and $name, $value and $checked represent the name,
+ * value and the checked status of the checkbox input, respectively.
+ * @return string the generated checkbox list
+ */
+ public static function checkboxList($name, $selection = null, $items = [], $options = [])
+ {
+ if (substr($name, -2) !== '[]') {
+ $name .= '[]';
+ }
+
+ $formatter = isset($options['item']) ? $options['item'] : null;
+ $itemOptions = isset($options['itemOptions']) ? $options['itemOptions'] : [];
+ $encode = !isset($options['encode']) || $options['encode'];
+ $lines = [];
+ $index = 0;
+ foreach ($items as $value => $label) {
+ $checked = $selection !== null &&
+ (!is_array($selection) && !strcmp($value, $selection)
+ || is_array($selection) && in_array($value, $selection));
+ if ($formatter !== null) {
+ $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
+ } else {
+ $lines[] = static::checkbox($name, $checked, array_merge($itemOptions, [
+ 'value' => $value,
+ 'label' => $encode ? static::encode($label) : $label,
+ ]));
+ }
+ $index++;
+ }
+
+ if (isset($options['unselect'])) {
+ // add a hidden field so that if the list box has no option being selected, it still submits a value
+ $name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;
+ $hidden = static::hiddenInput($name2, $options['unselect']);
+ } else {
+ $hidden = '';
+ }
+ $separator = isset($options['separator']) ? $options['separator'] : "\n";
+
+ $tag = isset($options['tag']) ? $options['tag'] : 'div';
+ unset($options['tag'], $options['unselect'], $options['encode'], $options['separator'], $options['item'], $options['itemOptions']);
+
+ return $hidden . static::tag($tag, implode($separator, $lines), $options);
+ }
+
+ /**
+ * Generates a list of radio buttons.
+ * A radio button list is like a checkbox list, except that it only allows single selection.
+ * @param string $name the name attribute of each radio button.
+ * @param string|array $selection the selected value(s).
+ * @param array $items the data item used to generate the radio buttons.
+ * The array values are the labels, while the array keys are the corresponding radio button values.
+ * @param array $options options (name => config) for the radio button list. The following options are supported:
+ *
+ * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
+ * By setting this option, a hidden input will be generated.
+ * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
+ * This option is ignored if `item` option is set.
+ * - separator: string, the HTML code that separates items.
+ * - itemOptions: array, the options for generating the radio button tag using [[radio()]].
+ * - item: callable, a callback that can be used to customize the generation of the HTML code
+ * corresponding to a single item in $items. The signature of this callback must be:
+ *
+ * ~~~
+ * function ($index, $label, $name, $checked, $value)
+ * ~~~
+ *
+ * where $index is the zero-based index of the radio button in the whole list; $label
+ * is the label for the radio button; and $name, $value and $checked represent the name,
+ * value and the checked status of the radio button input, respectively.
+ * @return string the generated radio button list
+ */
+ public static function radioList($name, $selection = null, $items = [], $options = [])
+ {
+ $encode = !isset($options['encode']) || $options['encode'];
+ $formatter = isset($options['item']) ? $options['item'] : null;
+ $itemOptions = isset($options['itemOptions']) ? $options['itemOptions'] : [];
+ $lines = [];
+ $index = 0;
+ foreach ($items as $value => $label) {
+ $checked = $selection !== null &&
+ (!is_array($selection) && !strcmp($value, $selection)
+ || is_array($selection) && in_array($value, $selection));
+ if ($formatter !== null) {
+ $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
+ } else {
+ $lines[] = static::radio($name, $checked, array_merge($itemOptions, [
+ 'value' => $value,
+ 'label' => $encode ? static::encode($label) : $label,
+ ]));
+ }
+ $index++;
+ }
+
+ $separator = isset($options['separator']) ? $options['separator'] : "\n";
+ if (isset($options['unselect'])) {
+ // add a hidden field so that if the list box has no option being selected, it still submits a value
+ $hidden = static::hiddenInput($name, $options['unselect']);
+ } else {
+ $hidden = '';
+ }
+
+ $tag = isset($options['tag']) ? $options['tag'] : 'div';
+ unset($options['tag'], $options['unselect'], $options['encode'], $options['separator'], $options['item'], $options['itemOptions']);
+
+ return $hidden . static::tag($tag, implode($separator, $lines), $options);
+ }
+
+ /**
+ * Generates an unordered list.
+ * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
+ * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
+ * @param array $options options (name => config) for the radio button list. The following options are supported:
+ *
+ * - encode: boolean, whether to HTML-encode the items. Defaults to true.
+ * This option is ignored if the `item` option is specified.
+ * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
+ * - item: callable, a callback that is used to generate each individual list item.
+ * The signature of this callback must be:
+ *
+ * ~~~
+ * function ($item, $index)
+ * ~~~
+ *
+ * where $index is the array key corresponding to `$item` in `$items`. The callback should return
+ * the whole list item tag.
+ *
+ * @return string the generated unordered list. An empty string is returned if `$items` is empty.
+ */
+ public static function ul($items, $options = [])
+ {
+ if (empty($items)) {
+ return '';
+ }
+ $tag = isset($options['tag']) ? $options['tag'] : 'ul';
+ $encode = !isset($options['encode']) || $options['encode'];
+ $formatter = isset($options['item']) ? $options['item'] : null;
+ $itemOptions = isset($options['itemOptions']) ? $options['itemOptions'] : [];
+ unset($options['tag'], $options['encode'], $options['item'], $options['itemOptions']);
+ $results = [];
+ foreach ($items as $index => $item) {
+ if ($formatter !== null) {
+ $results[] = call_user_func($formatter, $item, $index);
+ } else {
+ $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions);
+ }
+ }
+ return static::tag($tag, "\n" . implode("\n", $results) . "\n", $options);
+ }
+
+ /**
+ * Generates an ordered list.
+ * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
+ * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
+ * @param array $options options (name => config) for the radio button list. The following options are supported:
+ *
+ * - encode: boolean, whether to HTML-encode the items. Defaults to true.
+ * This option is ignored if the `item` option is specified.
+ * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
+ * - item: callable, a callback that is used to generate each individual list item.
+ * The signature of this callback must be:
+ *
+ * ~~~
+ * function ($item, $index)
+ * ~~~
+ *
+ * where $index is the array key corresponding to `$item` in `$items`. The callback should return
+ * the whole list item tag.
+ *
+ * @return string the generated ordered list. An empty string is returned if `$items` is empty.
+ */
+ public static function ol($items, $options = [])
+ {
+ $options['tag'] = 'ol';
+ return static::ul($items, $options);
+ }
+
+ /**
+ * Generates a label tag for the given model attribute.
+ * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]].
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * If a value is null, the corresponding attribute will not be rendered.
+ * The following options are specially handled:
+ *
+ * - label: this specifies the label to be displayed. Note that this will NOT be [[encoded()]].
+ * If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display
+ * (after encoding).
+ *
+ * @return string the generated label tag
+ */
+ public static function activeLabel($model, $attribute, $options = [])
+ {
+ $attribute = static::getAttributeName($attribute);
+ $label = isset($options['label']) ? $options['label'] : static::encode($model->getAttributeLabel($attribute));
+ $for = array_key_exists('for', $options) ? $options['for'] : static::getInputId($model, $attribute);
+ unset($options['label'], $options['for']);
+ return static::label($label, $for, $options);
+ }
+
+ /**
+ * Generates a tag that contains the first validation error of the specified model attribute.
+ * Note that even if there is no validation error, this method will still return an empty error tag.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded
+ * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * The following options are specially handled:
+ *
+ * - tag: this specifies the tag name. If not set, "div" will be used.
+ *
+ * @return string the generated label tag
+ */
+ public static function error($model, $attribute, $options = [])
+ {
+ $attribute = static::getAttributeName($attribute);
+ $error = $model->getFirstError($attribute);
+ $tag = isset($options['tag']) ? $options['tag'] : 'div';
+ unset($options['tag']);
+ return Html::tag($tag, Html::encode($error), $options);
+ }
+
+ /**
+ * Generates an input tag for the given model attribute.
+ * This method will generate the "name" and "value" tag attributes automatically for the model attribute
+ * unless they are explicitly specified in `$options`.
+ * @param string $type the input type (e.g. 'text', 'password')
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * @return string the generated input tag
+ */
+ public static function activeInput($type, $model, $attribute, $options = [])
+ {
+ $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
+ $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);
+ if (!array_key_exists('id', $options)) {
+ $options['id'] = static::getInputId($model, $attribute);
+ }
+ return static::input($type, $name, $value, $options);
+ }
+
+ /**
+ * Generates a text input tag for the given model attribute.
+ * This method will generate the "name" and "value" tag attributes automatically for the model attribute
+ * unless they are explicitly specified in `$options`.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * @return string the generated input tag
+ */
+ public static function activeTextInput($model, $attribute, $options = [])
+ {
+ return static::activeInput('text', $model, $attribute, $options);
+ }
+
+ /**
+ * Generates a hidden input tag for the given model attribute.
+ * This method will generate the "name" and "value" tag attributes automatically for the model attribute
+ * unless they are explicitly specified in `$options`.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * @return string the generated input tag
+ */
+ public static function activeHiddenInput($model, $attribute, $options = [])
+ {
+ return static::activeInput('hidden', $model, $attribute, $options);
+ }
+
+ /**
+ * Generates a password input tag for the given model attribute.
+ * This method will generate the "name" and "value" tag attributes automatically for the model attribute
+ * unless they are explicitly specified in `$options`.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * @return string the generated input tag
+ */
+ public static function activePasswordInput($model, $attribute, $options = [])
+ {
+ return static::activeInput('password', $model, $attribute, $options);
+ }
+
+ /**
+ * Generates a file input tag for the given model attribute.
+ * This method will generate the "name" and "value" tag attributes automatically for the model attribute
+ * unless they are explicitly specified in `$options`.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * @return string the generated input tag
+ */
+ public static function activeFileInput($model, $attribute, $options = [])
+ {
+ // add a hidden field so that if a model only has a file field, we can
+ // still use isset($_POST[$modelClass]) to detect if the input is submitted
+ return static::activeHiddenInput($model, $attribute, ['id' => null, 'value' => ''])
+ . static::activeInput('file', $model, $attribute, $options);
+ }
+
+ /**
+ * Generates a textarea tag for the given model attribute.
+ * The model attribute value will be used as the content in the textarea.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. These will be rendered as
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+ * @return string the generated textarea tag
+ */
+ public static function activeTextarea($model, $attribute, $options = [])
+ {
+ $name = static::getInputName($model, $attribute);
+ $value = static::getAttributeValue($model, $attribute);
+ if (!array_key_exists('id', $options)) {
+ $options['id'] = static::getInputId($model, $attribute);
+ }
+ return static::textarea($name, $value, $options);
+ }
+
+ /**
+ * Generates a radio button tag for the given model attribute.
+ * This method will generate the "checked" tag attribute according to the model attribute value.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - uncheck: string, the value associated with the uncheck state of the radio button. If not set,
+ * it will take the default value '0'. This method will render a hidden input so that if the radio button
+ * is not checked and is submitted, the value of this attribute will still be submitted to the server
+ * via the hidden input.
+ * - label: string, a label displayed next to the radio button. It will NOT be HTML-encoded. Therefore you can pass
+ * in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
+ * When this option is specified, the radio button will be enclosed by a label tag.
+ * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated radio button tag
+ */
+ public static function activeRadio($model, $attribute, $options = [])
+ {
+ $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
+ $checked = static::getAttributeValue($model, $attribute);
+ if (!array_key_exists('uncheck', $options)) {
+ $options['uncheck'] = '0';
+ }
+ if (!array_key_exists('id', $options)) {
+ $options['id'] = static::getInputId($model, $attribute);
+ }
+ return static::radio($name, $checked, $options);
+ }
+
+ /**
+ * Generates a checkbox tag for the given model attribute.
+ * This method will generate the "checked" tag attribute according to the model attribute value.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - uncheck: string, the value associated with the uncheck state of the radio button. If not set,
+ * it will take the default value '0'. This method will render a hidden input so that if the radio button
+ * is not checked and is submitted, the value of this attribute will still be submitted to the server
+ * via the hidden input.
+ * - label: string, a label displayed next to the checkbox. It will NOT be HTML-encoded. Therefore you can pass
+ * in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
+ * When this option is specified, the checkbox will be enclosed by a label tag.
+ * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated checkbox tag
+ */
+ public static function activeCheckbox($model, $attribute, $options = [])
+ {
+ $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
+ $checked = static::getAttributeValue($model, $attribute);
+ if (!array_key_exists('uncheck', $options)) {
+ $options['uncheck'] = '0';
+ }
+ if (!array_key_exists('id', $options)) {
+ $options['id'] = static::getInputId($model, $attribute);
+ }
+ return static::checkbox($name, $checked, $options);
+ }
+
+ /**
+ * Generates a drop-down list for the given model attribute.
+ * The selection of the drop-down list is taken from the value of the model attribute.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * [[\yii\helpers\ArrayHelper::map()]].
+ *
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - prompt: string, a prompt text to be displayed as the first option;
+ * - options: array, the attributes for the select option tags. The array keys must be valid option values,
+ * and the array values are the extra attributes for the corresponding option tags. For example,
+ *
+ * ~~~
+ * [
+ * 'value1' => ['disabled' => true],
+ * 'value2' => ['label' => 'value 2'],
+ * ];
+ * ~~~
+ *
+ * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
+ * except that the array keys represent the optgroup labels specified in $items.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated drop-down list tag
+ */
+ public static function activeDropDownList($model, $attribute, $items, $options = [])
+ {
+ $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
+ $selection = static::getAttributeValue($model, $attribute);
+ if (!array_key_exists('id', $options)) {
+ $options['id'] = static::getInputId($model, $attribute);
+ }
+ return static::dropDownList($name, $selection, $items, $options);
+ }
+
+ /**
+ * Generates a list box.
+ * The selection of the list box is taken from the value of the model attribute.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * [[\yii\helpers\ArrayHelper::map()]].
+ *
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - prompt: string, a prompt text to be displayed as the first option;
+ * - options: array, the attributes for the select option tags. The array keys must be valid option values,
+ * and the array values are the extra attributes for the corresponding option tags. For example,
+ *
+ * ~~~
+ * [
+ * 'value1' => ['disabled' => true],
+ * 'value2' => ['label' => 'value 2'],
+ * ];
+ * ~~~
+ *
+ * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
+ * except that the array keys represent the optgroup labels specified in $items.
+ * - unselect: string, the value that will be submitted when no option is selected.
+ * When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
+ * mode, we can still obtain the posted unselect value.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ *
+ * @return string the generated list box tag
+ */
+ public static function activeListBox($model, $attribute, $items, $options = [])
+ {
+ $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
+ $selection = static::getAttributeValue($model, $attribute);
+ if (!array_key_exists('unselect', $options)) {
+ $options['unselect'] = '0';
+ }
+ if (!array_key_exists('id', $options)) {
+ $options['id'] = static::getInputId($model, $attribute);
+ }
+ return static::listBox($name, $selection, $items, $options);
+ }
+
+ /**
+ * Generates a list of checkboxes.
+ * A checkbox list allows multiple selection, like [[listBox()]].
+ * As a result, the corresponding submitted value is an array.
+ * The selection of the checkbox list is taken from the value of the model attribute.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $items the data item used to generate the checkboxes.
+ * The array values are the labels, while the array keys are the corresponding checkbox values.
+ * Note that the labels will NOT be HTML-encoded, while the values will.
+ * @param array $options options (name => config) for the checkbox list. The following options are specially handled:
+ *
+ * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
+ * You may set this option to be null to prevent default value submission.
+ * If this option is not set, an empty string will be submitted.
+ * - separator: string, the HTML code that separates items.
+ * - item: callable, a callback that can be used to customize the generation of the HTML code
+ * corresponding to a single item in $items. The signature of this callback must be:
+ *
+ * ~~~
+ * function ($index, $label, $name, $checked, $value)
+ * ~~~
+ *
+ * where $index is the zero-based index of the checkbox in the whole list; $label
+ * is the label for the checkbox; and $name, $value and $checked represent the name,
+ * value and the checked status of the checkbox input.
+ * @return string the generated checkbox list
+ */
+ public static function activeCheckboxList($model, $attribute, $items, $options = [])
+ {
+ $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
+ $selection = static::getAttributeValue($model, $attribute);
+ if (!array_key_exists('unselect', $options)) {
+ $options['unselect'] = '';
+ }
+ if (!array_key_exists('id', $options)) {
+ $options['id'] = static::getInputId($model, $attribute);
+ }
+ return static::checkboxList($name, $selection, $items, $options);
+ }
+
+ /**
+ * Generates a list of radio buttons.
+ * A radio button list is like a checkbox list, except that it only allows single selection.
+ * The selection of the radio buttons is taken from the value of the model attribute.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
+ * about attribute expression.
+ * @param array $items the data item used to generate the radio buttons.
+ * The array keys are the labels, while the array values are the corresponding radio button values.
+ * Note that the labels will NOT be HTML-encoded, while the values will.
+ * @param array $options options (name => config) for the radio button list. The following options are specially handled:
+ *
+ * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
+ * You may set this option to be null to prevent default value submission.
+ * If this option is not set, an empty string will be submitted.
+ * - separator: string, the HTML code that separates items.
+ * - item: callable, a callback that can be used to customize the generation of the HTML code
+ * corresponding to a single item in $items. The signature of this callback must be:
+ *
+ * ~~~
+ * function ($index, $label, $name, $checked, $value)
+ * ~~~
+ *
+ * where $index is the zero-based index of the radio button in the whole list; $label
+ * is the label for the radio button; and $name, $value and $checked represent the name,
+ * value and the checked status of the radio button input.
+ * @return string the generated radio button list
+ */
+ public static function activeRadioList($model, $attribute, $items, $options = [])
+ {
+ $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
+ $selection = static::getAttributeValue($model, $attribute);
+ if (!array_key_exists('unselect', $options)) {
+ $options['unselect'] = '';
+ }
+ if (!array_key_exists('id', $options)) {
+ $options['id'] = static::getInputId($model, $attribute);
+ }
+ return static::radioList($name, $selection, $items, $options);
+ }
+
+ /**
+ * Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]].
+ * @param string|array $selection the selected value(s). This can be either a string for single selection
+ * or an array for multiple selections.
+ * @param array $items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * [[\yii\helpers\ArrayHelper::map()]].
+ *
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call.
+ * This method will take out these elements, if any: "prompt", "options" and "groups". See more details
+ * in [[dropDownList()]] for the explanation of these elements.
+ *
+ * @return string the generated list options
+ */
+ public static function renderSelectOptions($selection, $items, &$tagOptions = [])
+ {
+ $lines = [];
+ if (isset($tagOptions['prompt'])) {
+ $prompt = str_replace(' ', ' ', static::encode($tagOptions['prompt']));
+ $lines[] = static::tag('option', $prompt, ['value' => '']);
+ }
+
+ $options = isset($tagOptions['options']) ? $tagOptions['options'] : [];
+ $groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : [];
+ unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);
+
+ foreach ($items as $key => $value) {
+ if (is_array($value)) {
+ $groupAttrs = isset($groups[$key]) ? $groups[$key] : [];
+ $groupAttrs['label'] = $key;
+ $attrs = ['options' => $options, 'groups' => $groups];
+ $content = static::renderSelectOptions($selection, $value, $attrs);
+ $lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs);
+ } else {
+ $attrs = isset($options[$key]) ? $options[$key] : [];
+ $attrs['value'] = (string)$key;
+ $attrs['selected'] = $selection !== null &&
+ (!is_array($selection) && !strcmp($key, $selection)
+ || is_array($selection) && in_array($key, $selection));
+ $lines[] = static::tag('option', str_replace(' ', ' ', static::encode($value)), $attrs);
+ }
+ }
+
+ return implode("\n", $lines);
+ }
+
+ /**
+ * Renders the HTML tag attributes.
+ * Attributes whose values are of boolean type will be treated as
+ * [boolean attributes](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes).
+ * And attributes whose values are null will not be rendered.
+ * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]].
+ * @return string the rendering result. If the attributes are not empty, they will be rendered
+ * into a string with a leading white space (so that it can be directly appended to the tag name
+ * in a tag. If there is no attribute, an empty string will be returned.
+ */
+ public static function renderTagAttributes($attributes)
+ {
+ if (count($attributes) > 1) {
+ $sorted = [];
+ foreach (static::$attributeOrder as $name) {
+ if (isset($attributes[$name])) {
+ $sorted[$name] = $attributes[$name];
+ }
+ }
+ $attributes = array_merge($sorted, $attributes);
+ }
+
+ $html = '';
+ foreach ($attributes as $name => $value) {
+ if (is_bool($value)) {
+ if ($value) {
+ $html .= " $name";
+ }
+ } elseif ($value !== null) {
+ $html .= " $name=\"" . static::encode($value) . '"';
+ }
+ }
+ return $html;
+ }
+
+ /**
+ * Normalizes the input parameter to be a valid URL.
+ *
+ * If the input parameter
+ *
+ * - is an empty string: the currently requested URL will be returned;
+ * - is a non-empty string: it will first be processed by [[Yii::getAlias()]]. If the result
+ * is an absolute URL, it will be returned without any change further; Otherwise, the result
+ * will be prefixed with [[\yii\web\Request::baseUrl]] and returned.
+ * - is an array: the first array element is considered a route, while the rest of the name-value
+ * pairs are treated as the parameters to be used for URL creation using [[\yii\web\Controller::createUrl()]].
+ * For example: `['post/index', 'page' => 2]`, `['index']`.
+ * In case there is no controller, [[\yii\web\UrlManager::createUrl()]] will be used.
+ *
+ * @param array|string $url the parameter to be used to generate a valid URL
+ * @return string the normalized URL
+ * @throws InvalidParamException if the parameter is invalid.
+ */
+ public static function url($url)
+ {
+ if (is_array($url)) {
+ if (isset($url[0])) {
+ $route = $url[0];
+ $params = array_splice($url, 1);
+ if (Yii::$app->controller instanceof \yii\web\Controller) {
+ return Yii::$app->controller->createUrl($route, $params);
+ } else {
+ return Yii::$app->getUrlManager()->createUrl($route, $params);
+ }
+ } else {
+ throw new InvalidParamException('The array specifying a URL must contain at least one element.');
+ }
+ } elseif ($url === '') {
+ return Yii::$app->getRequest()->getUrl();
+ } else {
+ $url = Yii::getAlias($url);
+ if ($url !== '' && ($url[0] === '/' || $url[0] === '#' || strpos($url, '://'))) {
+ return $url;
+ } else {
+ return Yii::$app->getRequest()->getBaseUrl() . '/' . $url;
+ }
+ }
+ }
+
+ /**
+ * Adds a CSS class to the specified options.
+ * If the CSS class is already in the options, it will not be added again.
+ * @param array $options the options to be modified.
+ * @param string $class the CSS class to be added
+ */
+ public static function addCssClass(&$options, $class)
+ {
+ if (isset($options['class'])) {
+ $classes = ' ' . $options['class'] . ' ';
+ if (($pos = strpos($classes, ' ' . $class . ' ')) === false) {
+ $options['class'] .= ' ' . $class;
+ }
+ } else {
+ $options['class'] = $class;
+ }
+ }
+
+ /**
+ * Removes a CSS class from the specified options.
+ * @param array $options the options to be modified.
+ * @param string $class the CSS class to be removed
+ */
+ public static function removeCssClass(&$options, $class)
+ {
+ if (isset($options['class'])) {
+ $classes = array_unique(preg_split('/\s+/', $options['class'] . ' ' . $class, -1, PREG_SPLIT_NO_EMPTY));
+ if (($index = array_search($class, $classes)) !== false) {
+ unset($classes[$index]);
+ }
+ if (empty($classes)) {
+ unset($options['class']);
+ } else {
+ $options['class'] = implode(' ', $classes);
+ }
+ }
+ }
+
+ /**
+ * Returns the real attribute name from the given attribute expression.
+ *
+ * An attribute expression is an attribute name prefixed and/or suffixed with array indexes.
+ * It is mainly used in tabular data input and/or input of array type. Below are some examples:
+ *
+ * - `[0]content` is used in tabular data input to represent the "content" attribute
+ * for the first model in tabular input;
+ * - `dates[0]` represents the first array element of the "dates" attribute;
+ * - `[0]dates[0]` represents the first array element of the "dates" attribute
+ * for the first model in tabular input.
+ *
+ * If `$attribute` has neither prefix nor suffix, it will be returned back without change.
+ * @param string $attribute the attribute name or expression
+ * @return string the attribute name without prefix and suffix.
+ * @throws InvalidParamException if the attribute name contains non-word characters.
+ */
+ public static function getAttributeName($attribute)
+ {
+ if (preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) {
+ return $matches[2];
+ } else {
+ throw new InvalidParamException('Attribute name must contain word characters only.');
+ }
+ }
+
+ /**
+ * Returns the value of the specified attribute name or expression.
+ *
+ * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`.
+ * See [[getAttributeName()]] for more details about attribute expression.
+ *
+ * If an attribute value is an instance of [[ActiveRecordInterface]] or an array of such instances,
+ * the primary value(s) of the AR instance(s) will be returned instead.
+ *
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression
+ * @return string|array the corresponding attribute value
+ * @throws InvalidParamException if the attribute name contains non-word characters.
+ */
+ public static function getAttributeValue($model, $attribute)
+ {
+ if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) {
+ throw new InvalidParamException('Attribute name must contain word characters only.');
+ }
+ $attribute = $matches[2];
+ $value = $model->$attribute;
+ if ($matches[3] !== '') {
+ foreach (explode('][', trim($matches[3], '[]')) as $id) {
+ if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) {
+ $value = $value[$id];
+ } else {
+ return null;
+ }
+ }
+ }
+
+ // https://github.com/yiisoft/yii2/issues/1457
+ if (is_array($value)) {
+ foreach ($value as $i => $v) {
+ if ($v instanceof ActiveRecordInterface) {
+ $v = $v->getPrimaryKey(false);
+ $value[$i] = is_array($v) ? json_encode($v) : $v;
+ }
+ }
+ } elseif ($value instanceof ActiveRecordInterface) {
+ $value = $value->getPrimaryKey(false);
+ return is_array($value) ? json_encode($value) : $value;
+ }
+ return $value;
+ }
+
+ /**
+ * Generates an appropriate input name for the specified attribute name or expression.
+ *
+ * This method generates a name that can be used as the input name to collect user input
+ * for the specified attribute. The name is generated according to the [[Model::formName|form name]]
+ * of the model and the given attribute name. For example, if the form name of the `Post` model
+ * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`.
+ *
+ * See [[getAttributeName()]] for explanation of attribute expression.
+ *
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression
+ * @return string the generated input name
+ * @throws InvalidParamException if the attribute name contains non-word characters.
+ */
+ public static function getInputName($model, $attribute)
+ {
+ $formName = $model->formName();
+ if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) {
+ throw new InvalidParamException('Attribute name must contain word characters only.');
+ }
+ $prefix = $matches[1];
+ $attribute = $matches[2];
+ $suffix = $matches[3];
+ if ($formName === '' && $prefix === '') {
+ return $attribute . $suffix;
+ } elseif ($formName !== '') {
+ return $formName . $prefix . "[$attribute]" . $suffix;
+ } else {
+ throw new InvalidParamException(get_class($model) . '::formName() cannot be empty for tabular inputs.');
+ }
+ }
+
+ /**
+ * Generates an appropriate input ID for the specified attribute name or expression.
+ *
+ * This method converts the result [[getInputName()]] into a valid input ID.
+ * For example, if [[getInputName()]] returns `Post[content]`, this method will return `post-content`.
+ * @param Model $model the model object
+ * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression.
+ * @return string the generated input ID
+ * @throws InvalidParamException if the attribute name contains non-word characters.
+ */
+ public static function getInputId($model, $attribute)
+ {
+ $name = strtolower(static::getInputName($model, $attribute));
+ return str_replace(['[]', '][', '[', ']', ' '], ['', '-', '-', '', '-'], $name);
+ }
+}
diff --git a/framework/yii/helpers/BaseHtmlPurifier.php b/framework/yii/helpers/BaseHtmlPurifier.php
new file mode 100644
index 0000000..17d2122
--- /dev/null
+++ b/framework/yii/helpers/BaseHtmlPurifier.php
@@ -0,0 +1,34 @@
+
+ * @since 2.0
+ */
+class BaseHtmlPurifier
+{
+ /**
+ * Passes markup through HTMLPurifier making it safe to output to end user
+ *
+ * @param string $content
+ * @param array|null $config
+ * @return string
+ */
+ public static function process($content, $config = null)
+ {
+ $configInstance = \HTMLPurifier_Config::create($config);
+ $configInstance->autoFinalize = false;
+ $purifier=\HTMLPurifier::instance($configInstance);
+ $purifier->config->set('Cache.SerializerPath', \Yii::$app->getRuntimePath());
+ return $purifier->purify($content);
+ }
+}
diff --git a/framework/yii/helpers/BaseInflector.php b/framework/yii/helpers/BaseInflector.php
new file mode 100644
index 0000000..2f4f01b
--- /dev/null
+++ b/framework/yii/helpers/BaseInflector.php
@@ -0,0 +1,480 @@
+
+ * @since 2.0
+ */
+class BaseInflector
+{
+ /**
+ * @var array the rules for converting a word into its plural form.
+ * The keys are the regular expressions and the values are the corresponding replacements.
+ */
+ public static $plurals = [
+ '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media)$/i' => '\1',
+ '/^(sea[- ]bass)$/i' => '\1',
+ '/(m)ove$/i' => '\1oves',
+ '/(f)oot$/i' => '\1eet',
+ '/(h)uman$/i' => '\1umans',
+ '/(s)tatus$/i' => '\1tatuses',
+ '/(s)taff$/i' => '\1taff',
+ '/(t)ooth$/i' => '\1eeth',
+ '/(quiz)$/i' => '\1zes',
+ '/^(ox)$/i' => '\1\2en',
+ '/([m|l])ouse$/i' => '\1ice',
+ '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
+ '/(x|ch|ss|sh)$/i' => '\1es',
+ '/([^aeiouy]|qu)y$/i' => '\1ies',
+ '/(hive)$/i' => '\1s',
+ '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
+ '/sis$/i' => 'ses',
+ '/([ti])um$/i' => '\1a',
+ '/(p)erson$/i' => '\1eople',
+ '/(m)an$/i' => '\1en',
+ '/(c)hild$/i' => '\1hildren',
+ '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\1oes',
+ '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
+ '/us$/i' => 'uses',
+ '/(alias)$/i' => '\1es',
+ '/(ax|cris|test)is$/i' => '\1es',
+ '/s$/' => 's',
+ '/^$/' => '',
+ '/$/' => 's',
+ ];
+ /**
+ * @var array the rules for converting a word into its singular form.
+ * The keys are the regular expressions and the values are the corresponding replacements.
+ */
+ public static $singulars = [
+ '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media|ss)$/i' => '\1',
+ '/^(sea[- ]bass)$/i' => '\1',
+ '/(s)tatuses$/i' => '\1tatus',
+ '/(f)eet$/i' => '\1oot',
+ '/(t)eeth$/i' => '\1ooth',
+ '/^(.*)(menu)s$/i' => '\1\2',
+ '/(quiz)zes$/i' => '\\1',
+ '/(matr)ices$/i' => '\1ix',
+ '/(vert|ind)ices$/i' => '\1ex',
+ '/^(ox)en/i' => '\1',
+ '/(alias)(es)*$/i' => '\1',
+ '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
+ '/([ftw]ax)es/i' => '\1',
+ '/(cris|ax|test)es$/i' => '\1is',
+ '/(shoe|slave)s$/i' => '\1',
+ '/(o)es$/i' => '\1',
+ '/ouses$/' => 'ouse',
+ '/([^a])uses$/' => '\1us',
+ '/([m|l])ice$/i' => '\1ouse',
+ '/(x|ch|ss|sh)es$/i' => '\1',
+ '/(m)ovies$/i' => '\1\2ovie',
+ '/(s)eries$/i' => '\1\2eries',
+ '/([^aeiouy]|qu)ies$/i' => '\1y',
+ '/([lr])ves$/i' => '\1f',
+ '/(tive)s$/i' => '\1',
+ '/(hive)s$/i' => '\1',
+ '/(drive)s$/i' => '\1',
+ '/([^fo])ves$/i' => '\1fe',
+ '/(^analy)ses$/i' => '\1sis',
+ '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
+ '/([ti])a$/i' => '\1um',
+ '/(p)eople$/i' => '\1\2erson',
+ '/(m)en$/i' => '\1an',
+ '/(c)hildren$/i' => '\1\2hild',
+ '/(n)ews$/i' => '\1\2ews',
+ '/eaus$/' => 'eau',
+ '/^(.*us)$/' => '\\1',
+ '/s$/i' => '',
+ ];
+ /**
+ * @var array the special rules for converting a word between its plural form and singular form.
+ * The keys are the special words in singular form, and the values are the corresponding plural form.
+ */
+ public static $specials = [
+ 'atlas' => 'atlases',
+ 'beef' => 'beefs',
+ 'brother' => 'brothers',
+ 'cafe' => 'cafes',
+ 'child' => 'children',
+ 'cookie' => 'cookies',
+ 'corpus' => 'corpuses',
+ 'cow' => 'cows',
+ 'curve' => 'curves',
+ 'foe' => 'foes',
+ 'ganglion' => 'ganglions',
+ 'genie' => 'genies',
+ 'genus' => 'genera',
+ 'graffito' => 'graffiti',
+ 'hoof' => 'hoofs',
+ 'loaf' => 'loaves',
+ 'man' => 'men',
+ 'money' => 'monies',
+ 'mongoose' => 'mongooses',
+ 'move' => 'moves',
+ 'mythos' => 'mythoi',
+ 'niche' => 'niches',
+ 'numen' => 'numina',
+ 'occiput' => 'occiputs',
+ 'octopus' => 'octopuses',
+ 'opus' => 'opuses',
+ 'ox' => 'oxen',
+ 'penis' => 'penises',
+ 'sex' => 'sexes',
+ 'soliloquy' => 'soliloquies',
+ 'testis' => 'testes',
+ 'trilby' => 'trilbys',
+ 'turf' => 'turfs',
+ 'wave' => 'waves',
+ 'Amoyese' => 'Amoyese',
+ 'bison' => 'bison',
+ 'Borghese' => 'Borghese',
+ 'bream' => 'bream',
+ 'breeches' => 'breeches',
+ 'britches' => 'britches',
+ 'buffalo' => 'buffalo',
+ 'cantus' => 'cantus',
+ 'carp' => 'carp',
+ 'chassis' => 'chassis',
+ 'clippers' => 'clippers',
+ 'cod' => 'cod',
+ 'coitus' => 'coitus',
+ 'Congoese' => 'Congoese',
+ 'contretemps' => 'contretemps',
+ 'corps' => 'corps',
+ 'debris' => 'debris',
+ 'diabetes' => 'diabetes',
+ 'djinn' => 'djinn',
+ 'eland' => 'eland',
+ 'elk' => 'elk',
+ 'equipment' => 'equipment',
+ 'Faroese' => 'Faroese',
+ 'flounder' => 'flounder',
+ 'Foochowese' => 'Foochowese',
+ 'gallows' => 'gallows',
+ 'Genevese' => 'Genevese',
+ 'Genoese' => 'Genoese',
+ 'Gilbertese' => 'Gilbertese',
+ 'graffiti' => 'graffiti',
+ 'headquarters' => 'headquarters',
+ 'herpes' => 'herpes',
+ 'hijinks' => 'hijinks',
+ 'Hottentotese' => 'Hottentotese',
+ 'information' => 'information',
+ 'innings' => 'innings',
+ 'jackanapes' => 'jackanapes',
+ 'Kiplingese' => 'Kiplingese',
+ 'Kongoese' => 'Kongoese',
+ 'Lucchese' => 'Lucchese',
+ 'mackerel' => 'mackerel',
+ 'Maltese' => 'Maltese',
+ 'mews' => 'mews',
+ 'moose' => 'moose',
+ 'mumps' => 'mumps',
+ 'Nankingese' => 'Nankingese',
+ 'news' => 'news',
+ 'nexus' => 'nexus',
+ 'Niasese' => 'Niasese',
+ 'Pekingese' => 'Pekingese',
+ 'Piedmontese' => 'Piedmontese',
+ 'pincers' => 'pincers',
+ 'Pistoiese' => 'Pistoiese',
+ 'pliers' => 'pliers',
+ 'Portuguese' => 'Portuguese',
+ 'proceedings' => 'proceedings',
+ 'rabies' => 'rabies',
+ 'rice' => 'rice',
+ 'rhinoceros' => 'rhinoceros',
+ 'salmon' => 'salmon',
+ 'Sarawakese' => 'Sarawakese',
+ 'scissors' => 'scissors',
+ 'series' => 'series',
+ 'Shavese' => 'Shavese',
+ 'shears' => 'shears',
+ 'siemens' => 'siemens',
+ 'species' => 'species',
+ 'swine' => 'swine',
+ 'testes' => 'testes',
+ 'trousers' => 'trousers',
+ 'trout' => 'trout',
+ 'tuna' => 'tuna',
+ 'Vermontese' => 'Vermontese',
+ 'Wenchowese' => 'Wenchowese',
+ 'whiting' => 'whiting',
+ 'wildebeest' => 'wildebeest',
+ 'Yengeese' => 'Yengeese',
+ ];
+ /**
+ * @var array map of special chars and its translation. This is used by [[slug()]].
+ */
+ public static $transliteration = [
+ '/ä|æ|ǽ/' => 'ae',
+ '/ö|œ/' => 'oe',
+ '/ü/' => 'ue',
+ '/Ä/' => 'Ae',
+ '/Ü/' => 'Ue',
+ '/Ö/' => 'Oe',
+ '/À|Á|Â|Ã|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A',
+ '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a',
+ '/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
+ '/ç|ć|ĉ|ċ|č/' => 'c',
+ '/Ð|Ď|Đ/' => 'D',
+ '/ð|ď|đ/' => 'd',
+ '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E',
+ '/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e',
+ '/Ĝ|Ğ|Ġ|Ģ/' => 'G',
+ '/ĝ|ğ|ġ|ģ/' => 'g',
+ '/Ĥ|Ħ/' => 'H',
+ '/ĥ|ħ/' => 'h',
+ '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I',
+ '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i',
+ '/Ĵ/' => 'J',
+ '/ĵ/' => 'j',
+ '/Ķ/' => 'K',
+ '/ķ/' => 'k',
+ '/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L',
+ '/ĺ|ļ|ľ|ŀ|ł/' => 'l',
+ '/Ñ|Ń|Ņ|Ň/' => 'N',
+ '/ñ|ń|ņ|ň|ʼn/' => 'n',
+ '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O',
+ '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o',
+ '/Ŕ|Ŗ|Ř/' => 'R',
+ '/ŕ|ŗ|ř/' => 'r',
+ '/Ś|Ŝ|Ş|Ș|Š/' => 'S',
+ '/ś|ŝ|ş|ș|š|ſ/' => 's',
+ '/Ţ|Ț|Ť|Ŧ/' => 'T',
+ '/ţ|ț|ť|ŧ/' => 't',
+ '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U',
+ '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u',
+ '/Ý|Ÿ|Ŷ/' => 'Y',
+ '/ý|ÿ|ŷ/' => 'y',
+ '/Ŵ/' => 'W',
+ '/ŵ/' => 'w',
+ '/Ź|Ż|Ž/' => 'Z',
+ '/ź|ż|ž/' => 'z',
+ '/Æ|Ǽ/' => 'AE',
+ '/ß/' => 'ss',
+ '/IJ/' => 'IJ',
+ '/ij/' => 'ij',
+ '/Œ/' => 'OE',
+ '/ƒ/' => 'f'
+ ];
+
+ /**
+ * Converts a word to its plural form.
+ * Note that this is for English only!
+ * For example, 'apple' will become 'apples', and 'child' will become 'children'.
+ * @param string $word the word to be pluralized
+ * @return string the pluralized word
+ */
+ public static function pluralize($word)
+ {
+ if (isset(static::$specials[$word])) {
+ return static::$specials[$word];
+ }
+ foreach (static::$plurals as $rule => $replacement) {
+ if (preg_match($rule, $word)) {
+ return preg_replace($rule, $replacement, $word);
+ }
+ }
+ return $word;
+ }
+
+ /**
+ * Returns the singular of the $word
+ * @param string $word the english word to singularize
+ * @return string Singular noun.
+ */
+ public static function singularize($word)
+ {
+ $result = array_search($word, static::$specials, true);
+ if ($result !== false) {
+ return $result;
+ }
+ foreach (static::$singulars as $rule => $replacement) {
+ if (preg_match($rule, $word)) {
+ return preg_replace($rule, $replacement, $word);
+ }
+ }
+ return $word;
+ }
+
+ /**
+ * Converts an underscored or CamelCase word into a English
+ * sentence.
+ * @param string $words
+ * @param bool $ucAll whether to set all words to uppercase
+ * @return string
+ */
+ public static function titleize($words, $ucAll = false)
+ {
+ $words = static::humanize(static::underscore($words), $ucAll);
+ return $ucAll ? ucwords($words) : ucfirst($words);
+ }
+
+ /**
+ * Returns given word as CamelCased
+ * Converts a word like "send_email" to "SendEmail". It
+ * will remove non alphanumeric character from the word, so
+ * "who's online" will be converted to "WhoSOnline"
+ * @see variablize()
+ * @param string $word the word to CamelCase
+ * @return string
+ */
+ public static function camelize($word)
+ {
+ return str_replace(' ', '', ucwords(preg_replace('/[^A-Z^a-z^0-9]+/', ' ', $word)));
+ }
+
+ /**
+ * Converts a CamelCase name into space-separated words.
+ * For example, 'PostTag' will be converted to 'Post Tag'.
+ * @param string $name the string to be converted
+ * @param boolean $ucwords whether to capitalize the first letter in each word
+ * @return string the resulting words
+ */
+ public static function camel2words($name, $ucwords = true)
+ {
+ $label = trim(strtolower(str_replace([
+ '-',
+ '_',
+ '.'
+ ], ' ', preg_replace('/(? ' ',
+ '/\\s+/' => $replacement,
+ '/(?<=[a-z])([A-Z])/' => $replacement . '\\1',
+ str_replace(':rep', preg_quote($replacement, '/'), '/^[:rep]+|[:rep]+$/') => ''
+ ];
+ return preg_replace(array_keys($map), array_values($map), $string);
+ }
+
+ /**
+ * Converts a table name to its class name. For example, converts "people" to "Person"
+ * @param string $tableName
+ * @return string
+ */
+ public static function classify($tableName)
+ {
+ return static::camelize(static::singularize($tableName));
+ }
+
+ /**
+ * Converts number to its ordinal English form. For example, converts 13 to 13th, 2 to 2nd ...
+ * @param int $number the number to get its ordinal value
+ * @return string
+ */
+ public static function ordinalize($number)
+ {
+ if (in_array(($number % 100), range(11, 13))) {
+ return $number . 'th';
+ }
+ switch ($number % 10) {
+ case 1: return $number . 'st';
+ case 2: return $number . 'nd';
+ case 3: return $number . 'rd';
+ default: return $number . 'th';
+ }
+ }
+}
diff --git a/framework/yii/helpers/BaseJson.php b/framework/yii/helpers/BaseJson.php
new file mode 100644
index 0000000..019c7ef
--- /dev/null
+++ b/framework/yii/helpers/BaseJson.php
@@ -0,0 +1,110 @@
+
+ * @since 2.0
+ */
+class BaseJson
+{
+ /**
+ * Encodes the given value into a JSON string.
+ * The method enhances `json_encode()` by supporting JavaScript expressions.
+ * In particular, the method will not encode a JavaScript expression that is
+ * represented in terms of a [[JsExpression]] object.
+ * @param mixed $value the data to be encoded
+ * @param integer $options the encoding options. For more details please refer to
+ * [[http://www.php.net/manual/en/function.json-encode.php]]
+ * @return string the encoding result
+ */
+ public static function encode($value, $options = 0)
+ {
+ $expressions = [];
+ $value = static::processData($value, $expressions, uniqid());
+ $json = json_encode($value, $options);
+ return empty($expressions) ? $json : strtr($json, $expressions);
+ }
+
+ /**
+ * Decodes the given JSON string into a PHP data structure.
+ * @param string $json the JSON string to be decoded
+ * @param boolean $asArray whether to return objects in terms of associative arrays.
+ * @return mixed the PHP data
+ * @throws InvalidParamException if there is any decoding error
+ */
+ public static function decode($json, $asArray = true)
+ {
+ if (is_array($json)) {
+ throw new InvalidParamException('Invalid JSON data.');
+ }
+ $decode = json_decode((string)$json, $asArray);
+ switch (json_last_error()) {
+ case JSON_ERROR_NONE:
+ break;
+ case JSON_ERROR_DEPTH:
+ throw new InvalidParamException('The maximum stack depth has been exceeded.');
+ case JSON_ERROR_CTRL_CHAR:
+ throw new InvalidParamException('Control character error, possibly incorrectly encoded.');
+ case JSON_ERROR_SYNTAX:
+ throw new InvalidParamException('Syntax error.');
+ case JSON_ERROR_STATE_MISMATCH:
+ throw new InvalidParamException('Invalid or malformed JSON.');
+ case JSON_ERROR_UTF8:
+ throw new InvalidParamException('Malformed UTF-8 characters, possibly incorrectly encoded.');
+ default:
+ throw new InvalidParamException('Unknown JSON decoding error.');
+ }
+
+ return $decode;
+ }
+
+ /**
+ * Pre-processes the data before sending it to `json_encode()`.
+ * @param mixed $data the data to be processed
+ * @param array $expressions collection of JavaScript expressions
+ * @param string $expPrefix a prefix internally used to handle JS expressions
+ * @return mixed the processed data
+ */
+ protected static function processData($data, &$expressions, $expPrefix)
+ {
+ if ($data instanceof \JsonSerializable) {
+ return $data;
+ }
+
+ if (is_object($data)) {
+ if ($data instanceof JsExpression) {
+ $token = "!{[$expPrefix=" . count($expressions) . ']}!';
+ $expressions['"' . $token . '"'] = $data->expression;
+ return $token;
+ }
+ $data = $data instanceof Arrayable ? $data->toArray() : get_object_vars($data);
+ if ($data === [] && !$data instanceof Arrayable) {
+ return new \stdClass();
+ }
+ }
+
+ if (is_array($data)) {
+ foreach ($data as $key => $value) {
+ if (is_array($value) || is_object($value)) {
+ $data[$key] = static::processData($value, $expressions, $expPrefix);
+ }
+ }
+ }
+
+ return $data;
+ }
+}
diff --git a/framework/yii/helpers/BaseMarkdown.php b/framework/yii/helpers/BaseMarkdown.php
new file mode 100644
index 0000000..5258534
--- /dev/null
+++ b/framework/yii/helpers/BaseMarkdown.php
@@ -0,0 +1,44 @@
+
+ * @since 2.0
+ */
+class BaseMarkdown
+{
+ /**
+ * @var MarkdownExtra
+ */
+ protected static $markdown;
+
+ /**
+ * Converts markdown into HTML
+ *
+ * @param string $content
+ * @param array $config
+ * @return string
+ */
+ public static function process($content, $config = [])
+ {
+ if (static::$markdown === null) {
+ static::$markdown = new MarkdownExtra();
+ }
+ foreach ($config as $name => $value) {
+ static::$markdown->{$name} = $value;
+ }
+ return static::$markdown->transform($content);
+ }
+}
diff --git a/framework/yii/helpers/BaseSecurity.php b/framework/yii/helpers/BaseSecurity.php
new file mode 100644
index 0000000..491cb0e
--- /dev/null
+++ b/framework/yii/helpers/BaseSecurity.php
@@ -0,0 +1,354 @@
+
+ * @author Tom Worster
+ * @since 2.0
+ */
+class BaseSecurity
+{
+ /**
+ * Uses AES, block size is 128-bit (16 bytes).
+ */
+ const CRYPT_BLOCK_SIZE = 16;
+
+ /**
+ * Uses AES-192, key size is 192-bit (24 bytes).
+ */
+ const CRYPT_KEY_SIZE = 24;
+
+ /**
+ * Uses SHA-256.
+ */
+ const DERIVATION_HASH = 'sha256';
+
+ /**
+ * Uses 1000 iterations.
+ */
+ const DERIVATION_ITERATIONS = 1000;
+
+ /**
+ * Encrypts data.
+ * @param string $data data to be encrypted.
+ * @param string $password the encryption password
+ * @return string the encrypted data
+ * @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized
+ * @see decrypt()
+ */
+ public static function encrypt($data, $password)
+ {
+ $module = static::openCryptModule();
+ $data = static::addPadding($data);
+ srand();
+ $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
+ $key = static::deriveKey($password, $iv);
+ mcrypt_generic_init($module, $key, $iv);
+ $encrypted = $iv . mcrypt_generic($module, $data);
+ mcrypt_generic_deinit($module);
+ mcrypt_module_close($module);
+ return $encrypted;
+ }
+
+ /**
+ * Decrypts data
+ * @param string $data data to be decrypted.
+ * @param string $password the decryption password
+ * @return string the decrypted data
+ * @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized
+ * @see encrypt()
+ */
+ public static function decrypt($data, $password)
+ {
+ if ($data === null) {
+ return null;
+ }
+ $module = static::openCryptModule();
+ $ivSize = mcrypt_enc_get_iv_size($module);
+ $iv = StringHelper::byteSubstr($data, 0, $ivSize);
+ $key = static::deriveKey($password, $iv);
+ mcrypt_generic_init($module, $key, $iv);
+ $decrypted = mdecrypt_generic($module, StringHelper::byteSubstr($data, $ivSize, StringHelper::byteLength($data)));
+ mcrypt_generic_deinit($module);
+ mcrypt_module_close($module);
+ return static::stripPadding($decrypted);
+ }
+
+ /**
+ * Adds a padding to the given data (PKCS #7).
+ * @param string $data the data to pad
+ * @return string the padded data
+ */
+ protected static function addPadding($data)
+ {
+ $pad = self::CRYPT_BLOCK_SIZE - (StringHelper::byteLength($data) % self::CRYPT_BLOCK_SIZE);
+ return $data . str_repeat(chr($pad), $pad);
+ }
+
+ /**
+ * Strips the padding from the given data.
+ * @param string $data the data to trim
+ * @return string the trimmed data
+ */
+ protected static function stripPadding($data)
+ {
+ $end = StringHelper::byteSubstr($data, -1, NULL);
+ $last = ord($end);
+ $n = StringHelper::byteLength($data) - $last;
+ if (StringHelper::byteSubstr($data, $n, NULL) == str_repeat($end, $last)) {
+ return StringHelper::byteSubstr($data, 0, $n);
+ }
+ return false;
+ }
+
+ /**
+ * Derives a key from the given password (PBKDF2).
+ * @param string $password the source password
+ * @param string $salt the random salt
+ * @return string the derived key
+ */
+ protected static function deriveKey($password, $salt)
+ {
+ if (function_exists('hash_pbkdf2')) {
+ return hash_pbkdf2(self::DERIVATION_HASH, $password, $salt, self::DERIVATION_ITERATIONS, self::CRYPT_KEY_SIZE, true);
+ }
+ $hmac = hash_hmac(self::DERIVATION_HASH, $salt . pack('N', 1), $password, true);
+ $xorsum = $hmac;
+ for ($i = 1; $i < self::DERIVATION_ITERATIONS; $i++) {
+ $hmac = hash_hmac(self::DERIVATION_HASH, $hmac, $password, true);
+ $xorsum ^= $hmac;
+ }
+ return substr($xorsum, 0, self::CRYPT_KEY_SIZE);
+ }
+
+ /**
+ * Prefixes data with a keyed hash value so that it can later be detected if it is tampered.
+ * @param string $data the data to be protected
+ * @param string $key the secret key to be used for generating hash
+ * @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()"
+ * function to see the supported hashing algorithms on your system.
+ * @return string the data prefixed with the keyed hash
+ * @see validateData()
+ * @see getSecretKey()
+ */
+ public static function hashData($data, $key, $algorithm = 'sha256')
+ {
+ return hash_hmac($algorithm, $data, $key) . $data;
+ }
+
+ /**
+ * Validates if the given data is tampered.
+ * @param string $data the data to be validated. The data must be previously
+ * generated by [[hashData()]].
+ * @param string $key the secret key that was previously used to generate the hash for the data in [[hashData()]].
+ * @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()"
+ * function to see the supported hashing algorithms on your system. This must be the same
+ * as the value passed to [[hashData()]] when generating the hash for the data.
+ * @return string the real data with the hash stripped off. False if the data is tampered.
+ * @see hashData()
+ */
+ public static function validateData($data, $key, $algorithm = 'sha256')
+ {
+ $hashSize = StringHelper::byteLength(hash_hmac($algorithm, 'test', $key));
+ $n = StringHelper::byteLength($data);
+ if ($n >= $hashSize) {
+ $hash = StringHelper::byteSubstr($data, 0, $hashSize);
+ $data2 = StringHelper::byteSubstr($data, $hashSize, $n - $hashSize);
+ return $hash === hash_hmac($algorithm, $data2, $key) ? $data2 : false;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns a secret key associated with the specified name.
+ * If the secret key does not exist, a random key will be generated
+ * and saved in the file "keys.json" under the application's runtime directory
+ * so that the same secret key can be returned in future requests.
+ * @param string $name the name that is associated with the secret key
+ * @param integer $length the length of the key that should be generated if not exists
+ * @return string the secret key associated with the specified name
+ */
+ public static function getSecretKey($name, $length = 32)
+ {
+ static $keys;
+ $keyFile = Yii::$app->getRuntimePath() . '/keys.json';
+ if ($keys === null) {
+ $keys = [];
+ if (is_file($keyFile)) {
+ $keys = json_decode(file_get_contents($keyFile), true);
+ }
+ }
+ if (!isset($keys[$name])) {
+ $keys[$name] = static::generateRandomKey($length);
+ file_put_contents($keyFile, json_encode($keys));
+ }
+ return $keys[$name];
+ }
+
+ /**
+ * Generates a random key. The key may contain uppercase and lowercase latin letters, digits, underscore, dash and dot.
+ * @param integer $length the length of the key that should be generated
+ * @return string the generated random key
+ */
+ public static function generateRandomKey($length = 32)
+ {
+ if (function_exists('openssl_random_pseudo_bytes')) {
+ $key = strtr(base64_encode(openssl_random_pseudo_bytes($length, $strong)), '+/=', '_-.');
+ if ($strong) {
+ return substr($key, 0, $length);
+ }
+ }
+ $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.';
+ return substr(str_shuffle(str_repeat($chars, 5)), 0, $length);
+ }
+
+ /**
+ * Opens the mcrypt module.
+ * @return resource the mcrypt module handle.
+ * @throws InvalidConfigException if mcrypt extension is not installed
+ * @throws Exception if mcrypt initialization fails
+ */
+ protected static function openCryptModule()
+ {
+ if (!extension_loaded('mcrypt')) {
+ throw new InvalidConfigException('The mcrypt PHP extension is not installed.');
+ }
+ // AES uses a 128-bit block size
+ $module = @mcrypt_module_open('rijndael-128', '', 'cbc', '');
+ if ($module === false) {
+ throw new Exception('Failed to initialize the mcrypt module.');
+ }
+ return $module;
+ }
+
+ /**
+ * Generates a secure hash from a password and a random salt.
+ *
+ * The generated hash can be stored in database (e.g. `CHAR(64) CHARACTER SET latin1` on MySQL).
+ * Later when a password needs to be validated, the hash can be fetched and passed
+ * to [[validatePassword()]]. For example,
+ *
+ * ~~~
+ * // generates the hash (usually done during user registration or when the password is changed)
+ * $hash = Security::generatePasswordHash($password);
+ * // ...save $hash in database...
+ *
+ * // during login, validate if the password entered is correct using $hash fetched from database
+ * if (Security::validatePassword($password, $hash) {
+ * // password is good
+ * } else {
+ * // password is bad
+ * }
+ * ~~~
+ *
+ * @param string $password The password to be hashed.
+ * @param integer $cost Cost parameter used by the Blowfish hash algorithm.
+ * The higher the value of cost,
+ * the longer it takes to generate the hash and to verify a password against it. Higher cost
+ * therefore slows down a brute-force attack. For best protection against brute for attacks,
+ * set it to the highest value that is tolerable on production servers. The time taken to
+ * compute the hash doubles for every increment by one of $cost. So, for example, if the
+ * hash takes 1 second to compute when $cost is 14 then then the compute time varies as
+ * 2^($cost - 14) seconds.
+ * @throws Exception on bad password parameter or cost parameter
+ * @return string The password hash string, ASCII and not longer than 64 characters.
+ * @see validatePassword()
+ */
+ public static function generatePasswordHash($password, $cost = 13)
+ {
+ $salt = static::generateSalt($cost);
+ $hash = crypt($password, $salt);
+
+ if (!is_string($hash) || strlen($hash) < 32) {
+ throw new Exception('Unknown error occurred while generating hash.');
+ }
+
+ return $hash;
+ }
+
+ /**
+ * Verifies a password against a hash.
+ * @param string $password The password to verify.
+ * @param string $hash The hash to verify the password against.
+ * @return boolean whether the password is correct.
+ * @throws InvalidParamException on bad password or hash parameters or if crypt() with Blowfish hash is not available.
+ * @see generatePasswordHash()
+ */
+ public static function validatePassword($password, $hash)
+ {
+ if (!is_string($password) || $password === '') {
+ throw new InvalidParamException('Password must be a string and cannot be empty.');
+ }
+
+ if (!preg_match('/^\$2[axy]\$(\d\d)\$[\.\/0-9A-Za-z]{22}/', $hash, $matches) || $matches[1] < 4 || $matches[1] > 30) {
+ throw new InvalidParamException('Hash is invalid.');
+ }
+
+ $test = crypt($password, $hash);
+ $n = strlen($test);
+ if ($n < 32 || $n !== strlen($hash)) {
+ return false;
+ }
+
+ // Use a for-loop to compare two strings to prevent timing attacks. See:
+ // http://codereview.stackexchange.com/questions/13512
+ $check = 0;
+ for ($i = 0; $i < $n; ++$i) {
+ $check |= (ord($test[$i]) ^ ord($hash[$i]));
+ }
+
+ return $check === 0;
+ }
+
+ /**
+ * Generates a salt that can be used to generate a password hash.
+ *
+ * The PHP [crypt()](http://php.net/manual/en/function.crypt.php) built-in function
+ * requires, for the Blowfish hash algorithm, a salt string in a specific format:
+ * "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters
+ * from the alphabet "./0-9A-Za-z".
+ *
+ * @param integer $cost the cost parameter
+ * @return string the random salt value.
+ * @throws InvalidParamException if the cost parameter is not between 4 and 30
+ */
+ protected static function generateSalt($cost = 13)
+ {
+ $cost = (int)$cost;
+ if ($cost < 4 || $cost > 31) {
+ throw new InvalidParamException('Cost must be between 4 and 31.');
+ }
+
+ // Get 20 * 8bits of pseudo-random entropy from mt_rand().
+ $rand = '';
+ for ($i = 0; $i < 20; ++$i) {
+ $rand .= chr(mt_rand(0, 255));
+ }
+
+ // Add the microtime for a little more entropy.
+ $rand .= microtime();
+ // Mix the bits cryptographically into a 20-byte binary string.
+ $rand = sha1($rand, true);
+ // Form the prefix that specifies Blowfish algorithm and cost parameter.
+ $salt = sprintf("$2y$%02d$", $cost);
+ // Append the random salt data in the required base64 format.
+ $salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22));
+ return $salt;
+ }
+}
diff --git a/framework/yii/helpers/BaseStringHelper.php b/framework/yii/helpers/BaseStringHelper.php
new file mode 100644
index 0000000..e282913
--- /dev/null
+++ b/framework/yii/helpers/BaseStringHelper.php
@@ -0,0 +1,140 @@
+
+ * @author Alex Makarov
+ * @since 2.0
+ */
+class BaseStringHelper
+{
+ /**
+ * Returns the number of bytes in the given string.
+ * This method ensures the string is treated as a byte array by using `mb_strlen()`.
+ * @param string $string the string being measured for length
+ * @return integer the number of bytes in the given string.
+ */
+ public static function byteLength($string)
+ {
+ return mb_strlen($string, '8bit');
+ }
+
+ /**
+ * Returns the portion of string specified by the start and length parameters.
+ * This method ensures the string is treated as a byte array by using `mb_substr()`.
+ * @param string $string the input string. Must be one character or longer.
+ * @param integer $start the starting position
+ * @param integer $length the desired portion length
+ * @return string the extracted part of string, or FALSE on failure or an empty string.
+ * @see http://www.php.net/manual/en/function.substr.php
+ */
+ public static function byteSubstr($string, $start, $length)
+ {
+ return mb_substr($string, $start, $length, '8bit');
+ }
+
+ /**
+ * Returns the trailing name component of a path.
+ * This method is similar to the php function `basename()` except that it will
+ * treat both \ and / as directory separators, independent of the operating system.
+ * This method was mainly created to work on php namespaces. When working with real
+ * file paths, php's `basename()` should work fine for you.
+ * Note: this method is not aware of the actual filesystem, or path components such as "..".
+ *
+ * @param string $path A path string.
+ * @param string $suffix If the name component ends in suffix this will also be cut off.
+ * @return string the trailing name component of the given path.
+ * @see http://www.php.net/manual/en/function.basename.php
+ */
+ public static function basename($path, $suffix = '')
+ {
+ if (($len = mb_strlen($suffix)) > 0 && mb_substr($path, -$len) == $suffix) {
+ $path = mb_substr($path, 0, -$len);
+ }
+ $path = rtrim(str_replace('\\', '/', $path), '/\\');
+ if (($pos = mb_strrpos($path, '/')) !== false) {
+ return mb_substr($path, $pos + 1);
+ }
+ return $path;
+ }
+
+ /**
+ * Returns parent directory's path.
+ * This method is similar to `dirname()` except that it will treat
+ * both \ and / as directory separators, independent of the operating system.
+ *
+ * @param string $path A path string.
+ * @return string the parent directory's path.
+ * @see http://www.php.net/manual/en/function.basename.php
+ */
+ public static function dirname($path)
+ {
+ $pos = mb_strrpos(str_replace('\\', '/', $path), '/');
+ if ($pos !== false) {
+ return mb_substr($path, 0, $pos);
+ } else {
+ return $path;
+ }
+ }
+
+ /**
+ * Compares two strings or string arrays, and return their differences.
+ * This is a wrapper of the [phpspec/php-diff](https://packagist.org/packages/phpspec/php-diff) package.
+ * @param string|array $lines1 the first string or string array to be compared. If it is a string,
+ * it will be converted into a string array by breaking at newlines.
+ * @param string|array $lines2 the second string or string array to be compared. If it is a string,
+ * it will be converted into a string array by breaking at newlines.
+ * @param string $format the output format. It must be 'inline', 'unified', 'context', 'side-by-side', or 'array'.
+ * @return string|array the comparison result. An array is returned if `$format` is 'array'. For all other
+ * formats, a string is returned.
+ * @throws InvalidParamException if the format is invalid.
+ */
+ public static function diff($lines1, $lines2, $format = 'inline')
+ {
+ if (!is_array($lines1)) {
+ $lines1 = explode("\n", $lines1);
+ }
+ if (!is_array($lines2)) {
+ $lines2 = explode("\n", $lines2);
+ }
+ foreach ($lines1 as $i => $line) {
+ $lines1[$i] = rtrim($line, "\r\n");
+ }
+ foreach ($lines2 as $i => $line) {
+ $lines2[$i] = rtrim($line, "\r\n");
+ }
+ switch ($format) {
+ case 'inline':
+ $renderer = new \Diff_Renderer_Html_Inline();
+ break;
+ case 'array':
+ $renderer = new \Diff_Renderer_Html_Array();
+ break;
+ case 'side-by-side':
+ $renderer = new \Diff_Renderer_Html_SideBySide();
+ break;
+ case 'context':
+ $renderer = new \Diff_Renderer_Text_Context();
+ break;
+ case 'unified':
+ $renderer = new \Diff_Renderer_Text_Unified();
+ break;
+ default:
+ throw new InvalidParamException("Output format must be 'inline', 'side-by-side', 'array', 'context' or 'unified'.");
+ }
+ $diff = new \Diff($lines1, $lines2);
+ return $diff->render($renderer);
+ }
+}
diff --git a/framework/yii/helpers/BaseVarDumper.php b/framework/yii/helpers/BaseVarDumper.php
new file mode 100644
index 0000000..36b739c
--- /dev/null
+++ b/framework/yii/helpers/BaseVarDumper.php
@@ -0,0 +1,126 @@
+
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright © 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\helpers;
+
+/**
+ * BaseVarDumper provides concrete implementation for [[VarDumper]].
+ *
+ * Do not use BaseVarDumper. Use [[VarDumper]] instead.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class BaseVarDumper
+{
+ private static $_objects;
+ private static $_output;
+ private static $_depth;
+
+ /**
+ * Displays a variable.
+ * This method achieves the similar functionality as var_dump and print_r
+ * but is more robust when handling complex objects such as Yii controllers.
+ * @param mixed $var variable to be dumped
+ * @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10.
+ * @param boolean $highlight whether the result should be syntax-highlighted
+ */
+ public static function dump($var, $depth = 10, $highlight = false)
+ {
+ echo static::dumpAsString($var, $depth, $highlight);
+ }
+
+ /**
+ * Dumps a variable in terms of a string.
+ * This method achieves the similar functionality as var_dump and print_r
+ * but is more robust when handling complex objects such as Yii controllers.
+ * @param mixed $var variable to be dumped
+ * @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10.
+ * @param boolean $highlight whether the result should be syntax-highlighted
+ * @return string the string representation of the variable
+ */
+ public static function dumpAsString($var, $depth = 10, $highlight = false)
+ {
+ self::$_output = '';
+ self::$_objects = [];
+ self::$_depth = $depth;
+ self::dumpInternal($var, 0);
+ if ($highlight) {
+ $result = highlight_string("/', '', $result, 1);
+ }
+ return self::$_output;
+ }
+
+ /**
+ * @param mixed $var variable to be dumped
+ * @param integer $level depth level
+ */
+ private static function dumpInternal($var, $level)
+ {
+ switch (gettype($var)) {
+ case 'boolean':
+ self::$_output .= $var ? 'true' : 'false';
+ break;
+ case 'integer':
+ self::$_output .= "$var";
+ break;
+ case 'double':
+ self::$_output .= "$var";
+ break;
+ case 'string':
+ self::$_output .= "'" . addslashes($var) . "'";
+ break;
+ case 'resource':
+ self::$_output .= '{resource}';
+ break;
+ case 'NULL':
+ self::$_output .= "null";
+ break;
+ case 'unknown type':
+ self::$_output .= '{unknown}';
+ break;
+ case 'array':
+ if (self::$_depth <= $level) {
+ self::$_output .= '[...]';
+ } elseif (empty($var)) {
+ self::$_output .= '[]';
+ } else {
+ $keys = array_keys($var);
+ $spaces = str_repeat(' ', $level * 4);
+ self::$_output .= '[';
+ foreach ($keys as $key) {
+ self::$_output .= "\n" . $spaces . ' ';
+ self::dumpInternal($key, 0);
+ self::$_output .= ' => ';
+ self::dumpInternal($var[$key], $level + 1);
+ }
+ self::$_output .= "\n" . $spaces . ']';
+ }
+ break;
+ case 'object':
+ if (($id = array_search($var, self::$_objects, true)) !== false) {
+ self::$_output .= get_class($var) . '#' . ($id + 1) . '(...)';
+ } elseif (self::$_depth <= $level) {
+ self::$_output .= get_class($var) . '(...)';
+ } else {
+ $id = array_push(self::$_objects, $var);
+ $className = get_class($var);
+ $spaces = str_repeat(' ', $level * 4);
+ self::$_output .= "$className#$id\n" . $spaces . '(';
+ foreach ((array)$var as $key => $value) {
+ $keyDisplay = strtr(trim($key), ["\0" => ':']);
+ self::$_output .= "\n" . $spaces . " [$keyDisplay] => ";
+ self::dumpInternal($value, $level + 1);
+ }
+ self::$_output .= "\n" . $spaces . ')';
+ }
+ break;
+ }
+ }
+}
diff --git a/framework/yii/helpers/Console.php b/framework/yii/helpers/Console.php
index 0055107..a34dc96 100644
--- a/framework/yii/helpers/Console.php
+++ b/framework/yii/helpers/Console.php
@@ -8,15 +8,12 @@
namespace yii\helpers;
/**
- * TODO adjust phpdoc
- * Console View is the base class for console view components
- *
- * A console view provides functionality to create rich console application by allowing to format output
- * by adding color and font style to it.
+ * Console helper provides useful methods for command line related tasks such as getting input or formatting and coloring
+ * output.
*
* @author Carsten Brandt
* @since 2.0
*/
-class Console extends base\Console
+class Console extends BaseConsole
{
}
diff --git a/framework/yii/helpers/FileHelper.php b/framework/yii/helpers/FileHelper.php
index 04ce4e1..63954a4 100644
--- a/framework/yii/helpers/FileHelper.php
+++ b/framework/yii/helpers/FileHelper.php
@@ -16,6 +16,6 @@ namespace yii\helpers;
* @author Alex Makarov
* @since 2.0
*/
-class FileHelper extends base\FileHelper
+class FileHelper extends BaseFileHelper
{
}
diff --git a/framework/yii/helpers/Html.php b/framework/yii/helpers/Html.php
index b3a0743..f4fbbba 100644
--- a/framework/yii/helpers/Html.php
+++ b/framework/yii/helpers/Html.php
@@ -13,6 +13,6 @@ namespace yii\helpers;
* @author Qiang Xue
* @since 2.0
*/
-class Html extends base\Html
+class Html extends BaseHtml
{
}
diff --git a/framework/yii/helpers/HtmlPurifier.php b/framework/yii/helpers/HtmlPurifier.php
index 1173091..f2852ad 100644
--- a/framework/yii/helpers/HtmlPurifier.php
+++ b/framework/yii/helpers/HtmlPurifier.php
@@ -19,16 +19,19 @@ namespace yii\helpers;
* If you want to configure it:
*
* ```php
- * echo HtmlPurifier::process($html, array(
+ * echo HtmlPurifier::process($html, [
* 'Attr.EnableID' => true,
- * ));
+ * ]);
* ```
*
* For more details please refer to HTMLPurifier documentation](http://htmlpurifier.org/).
*
+ * Note that you should add `ezyang/htmlpurifier` to your composer.json `require` section and run `composer install`
+ * before using it.
+ *
* @author Alexander Makarov
* @since 2.0
*/
-class HtmlPurifier extends base\HtmlPurifier
+class HtmlPurifier extends BaseHtmlPurifier
{
}
diff --git a/framework/yii/helpers/Inflector.php b/framework/yii/helpers/Inflector.php
index 72f77a7..ab4713e 100644
--- a/framework/yii/helpers/Inflector.php
+++ b/framework/yii/helpers/Inflector.php
@@ -13,6 +13,6 @@ namespace yii\helpers;
* @author Antonio Ramirez
* @since 2.0
*/
-class Inflector extends base\Inflector
+class Inflector extends BaseInflector
{
}
diff --git a/framework/yii/helpers/Json.php b/framework/yii/helpers/Json.php
index 117db1f..8ca436a 100644
--- a/framework/yii/helpers/Json.php
+++ b/framework/yii/helpers/Json.php
@@ -8,10 +8,12 @@
namespace yii\helpers;
/**
- *
+ * Json is a helper class providing JSON data encoding and decoding.
+ * It enhances the PHP built-in functions `json_encode()` and `json_decode()`
+ * by supporting encoding JavaScript expressions and throwing exceptions when decoding fails.
* @author Qiang Xue
* @since 2.0
*/
-class Json extends base\Json
+class Json extends BaseJson
{
}
diff --git a/framework/yii/helpers/Markdown.php b/framework/yii/helpers/Markdown.php
index 3f6c98e..326cd34 100644
--- a/framework/yii/helpers/Markdown.php
+++ b/framework/yii/helpers/Markdown.php
@@ -19,15 +19,17 @@ namespace yii\helpers;
* If you want to configure the parser:
*
* ```php
- * $myHtml = Markdown::process($myText, array(
+ * $myHtml = Markdown::process($myText, [
* 'fn_id_prefix' => 'footnote_',
- * ));
+ * ]);
* ```
*
+ * Note that in order to use this helper you need to install "michelf/php-markdown" Composer package.
+ *
* For more details please refer to [PHP Markdown library documentation](http://michelf.ca/projects/php-markdown/).
* @author Alexander Makarov
* @since 2.0
*/
-class Markdown extends base\Markdown
+class Markdown extends BaseMarkdown
{
}
diff --git a/framework/yii/helpers/Security.php b/framework/yii/helpers/Security.php
new file mode 100644
index 0000000..0e3ee38
--- /dev/null
+++ b/framework/yii/helpers/Security.php
@@ -0,0 +1,29 @@
+
+ * @author Tom Worster
+ * @since 2.0
+ */
+class Security extends BaseSecurity
+{
+}
diff --git a/framework/yii/helpers/SecurityHelper.php b/framework/yii/helpers/SecurityHelper.php
deleted file mode 100644
index d16e7e6..0000000
--- a/framework/yii/helpers/SecurityHelper.php
+++ /dev/null
@@ -1,29 +0,0 @@
-
- * @author Tom Worster
- * @since 2.0
- */
-class SecurityHelper extends base\SecurityHelper
-{
-}
diff --git a/framework/yii/helpers/StringHelper.php b/framework/yii/helpers/StringHelper.php
index 22b881a..5ecd390 100644
--- a/framework/yii/helpers/StringHelper.php
+++ b/framework/yii/helpers/StringHelper.php
@@ -14,6 +14,6 @@ namespace yii\helpers;
* @author Alex Makarov
* @since 2.0
*/
-class StringHelper extends base\StringHelper
+class StringHelper extends BaseStringHelper
{
}
diff --git a/framework/yii/helpers/VarDumper.php b/framework/yii/helpers/VarDumper.php
index 59a1718..1ac5aa7 100644
--- a/framework/yii/helpers/VarDumper.php
+++ b/framework/yii/helpers/VarDumper.php
@@ -23,6 +23,6 @@ namespace yii\helpers;
* @author Qiang Xue
* @since 2.0
*/
-class VarDumper extends base\VarDumper
+class VarDumper extends BaseVarDumper
{
}
diff --git a/framework/yii/helpers/base/ArrayHelper.php b/framework/yii/helpers/base/ArrayHelper.php
deleted file mode 100644
index cee39bc..0000000
--- a/framework/yii/helpers/base/ArrayHelper.php
+++ /dev/null
@@ -1,381 +0,0 @@
-
- * @since 2.0
- */
-class ArrayHelper
-{
- /**
- * Merges two or more arrays into one recursively.
- * If each array has an element with the same string key value, the latter
- * will overwrite the former (different from array_merge_recursive).
- * Recursive merging will be conducted if both arrays have an element of array
- * type and are having the same key.
- * For integer-keyed elements, the elements from the latter array will
- * be appended to the former array.
- * @param array $a array to be merged to
- * @param array $b array to be merged from. You can specify additional
- * arrays via third argument, fourth argument etc.
- * @return array the merged array (the original arrays are not changed.)
- */
- public static function merge($a, $b)
- {
- $args = func_get_args();
- $res = array_shift($args);
- while (!empty($args)) {
- $next = array_shift($args);
- foreach ($next as $k => $v) {
- if (is_integer($k)) {
- isset($res[$k]) ? $res[] = $v : $res[$k] = $v;
- } elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) {
- $res[$k] = self::merge($res[$k], $v);
- } else {
- $res[$k] = $v;
- }
- }
- }
- return $res;
- }
-
- /**
- * Retrieves the value of an array element or object property with the given key or property name.
- * If the key does not exist in the array, the default value will be returned instead.
- *
- * Below are some usage examples,
- *
- * ~~~
- * // working with array
- * $username = \yii\helpers\ArrayHelper::getValue($_POST, 'username');
- * // working with object
- * $username = \yii\helpers\ArrayHelper::getValue($user, 'username');
- * // working with anonymous function
- * $fullName = \yii\helpers\ArrayHelper::getValue($user, function($user, $defaultValue) {
- * return $user->firstName . ' ' . $user->lastName;
- * });
- * ~~~
- *
- * @param array|object $array array or object to extract value from
- * @param string|\Closure $key key name of the array element, or property name of the object,
- * or an anonymous function returning the value. The anonymous function signature should be:
- * `function($array, $defaultValue)`.
- * @param mixed $default the default value to be returned if the specified key does not exist
- * @return mixed the value of the
- */
- public static function getValue($array, $key, $default = null)
- {
- if ($key instanceof \Closure) {
- return $key($array, $default);
- } elseif (is_array($array)) {
- return isset($array[$key]) || array_key_exists($key, $array) ? $array[$key] : $default;
- } else {
- return $array->$key;
- }
- }
-
- /**
- * Removes an item from an array and returns the value. If the key does not exist in the array, the default value
- * will be returned instead.
- *
- * Usage examples,
- *
- * ~~~
- * // $array = array('type' => 'A', 'options' => array(1, 2));
- * // working with array
- * $type = \yii\helpers\ArrayHelper::remove($array, 'type');
- * // $array content
- * // $array = array('options' => array(1, 2));
- * ~~~
- *
- * @param array $array the array to extract value from
- * @param string $key key name of the array element
- * @param mixed $default the default value to be returned if the specified key does not exist
- * @return mixed|null the value of the element if found, default value otherwise
- */
- public static function remove(&$array, $key, $default = null)
- {
- if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) {
- $value = $array[$key];
- unset($array[$key]);
- return $value;
- }
- return $default;
- }
-
- /**
- * Indexes an array according to a specified key.
- * The input array should be multidimensional or an array of objects.
- *
- * The key can be a key name of the sub-array, a property name of object, or an anonymous
- * function which returns the key value given an array element.
- *
- * If a key value is null, the corresponding array element will be discarded and not put in the result.
- *
- * For example,
- *
- * ~~~
- * $array = array(
- * array('id' => '123', 'data' => 'abc'),
- * array('id' => '345', 'data' => 'def'),
- * );
- * $result = ArrayHelper::index($array, 'id');
- * // the result is:
- * // array(
- * // '123' => array('id' => '123', 'data' => 'abc'),
- * // '345' => array('id' => '345', 'data' => 'def'),
- * // )
- *
- * // using anonymous function
- * $result = ArrayHelper::index($array, function(element) {
- * return $element['id'];
- * });
- * ~~~
- *
- * @param array $array the array that needs to be indexed
- * @param string|\Closure $key the column name or anonymous function whose result will be used to index the array
- * @return array the indexed array
- */
- public static function index($array, $key)
- {
- $result = array();
- foreach ($array as $element) {
- $value = static::getValue($element, $key);
- $result[$value] = $element;
- }
- return $result;
- }
-
- /**
- * Returns the values of a specified column in an array.
- * The input array should be multidimensional or an array of objects.
- *
- * For example,
- *
- * ~~~
- * $array = array(
- * array('id' => '123', 'data' => 'abc'),
- * array('id' => '345', 'data' => 'def'),
- * );
- * $result = ArrayHelper::getColumn($array, 'id');
- * // the result is: array( '123', '345')
- *
- * // using anonymous function
- * $result = ArrayHelper::getColumn($array, function(element) {
- * return $element['id'];
- * });
- * ~~~
- *
- * @param array $array
- * @param string|\Closure $name
- * @param boolean $keepKeys whether to maintain the array keys. If false, the resulting array
- * will be re-indexed with integers.
- * @return array the list of column values
- */
- public static function getColumn($array, $name, $keepKeys = true)
- {
- $result = array();
- if ($keepKeys) {
- foreach ($array as $k => $element) {
- $result[$k] = static::getValue($element, $name);
- }
- } else {
- foreach ($array as $element) {
- $result[] = static::getValue($element, $name);
- }
- }
-
- return $result;
- }
-
- /**
- * Builds a map (key-value pairs) from a multidimensional array or an array of objects.
- * The `$from` and `$to` parameters specify the key names or property names to set up the map.
- * Optionally, one can further group the map according to a grouping field `$group`.
- *
- * For example,
- *
- * ~~~
- * $array = array(
- * array('id' => '123', 'name' => 'aaa', 'class' => 'x'),
- * array('id' => '124', 'name' => 'bbb', 'class' => 'x'),
- * array('id' => '345', 'name' => 'ccc', 'class' => 'y'),
- * );
- *
- * $result = ArrayHelper::map($array, 'id', 'name');
- * // the result is:
- * // array(
- * // '123' => 'aaa',
- * // '124' => 'bbb',
- * // '345' => 'ccc',
- * // )
- *
- * $result = ArrayHelper::map($array, 'id', 'name', 'class');
- * // the result is:
- * // array(
- * // 'x' => array(
- * // '123' => 'aaa',
- * // '124' => 'bbb',
- * // ),
- * // 'y' => array(
- * // '345' => 'ccc',
- * // ),
- * // )
- * ~~~
- *
- * @param array $array
- * @param string|\Closure $from
- * @param string|\Closure $to
- * @param string|\Closure $group
- * @return array
- */
- public static function map($array, $from, $to, $group = null)
- {
- $result = array();
- foreach ($array as $element) {
- $key = static::getValue($element, $from);
- $value = static::getValue($element, $to);
- if ($group !== null) {
- $result[static::getValue($element, $group)][$key] = $value;
- } else {
- $result[$key] = $value;
- }
- }
- return $result;
- }
-
- /**
- * Sorts an array of objects or arrays (with the same structure) by one or several keys.
- * @param array $array the array to be sorted. The array will be modified after calling this method.
- * @param string|\Closure|array $key the key(s) to be sorted by. This refers to a key name of the sub-array
- * elements, a property name of the objects, or an anonymous function returning the values for comparison
- * purpose. The anonymous function signature should be: `function($item)`.
- * To sort by multiple keys, provide an array of keys here.
- * @param boolean|array $descending whether to sort in descending or ascending order. When
- * sorting by multiple keys with different descending orders, use an array of descending flags.
- * @param integer|array $sortFlag the PHP sort flag. Valid values include
- * `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING` and `SORT_LOCALE_STRING`.
- * Please refer to [PHP manual](http://php.net/manual/en/function.sort.php)
- * for more details. When sorting by multiple keys with different sort flags, use an array of sort flags.
- * @param boolean|array $caseSensitive whether to sort string in case-sensitive manner. This parameter
- * is used only when `$sortFlag` is `SORT_STRING`.
- * When sorting by multiple keys with different case sensitivities, use an array of boolean values.
- * @throws InvalidParamException if the $descending or $sortFlag parameters do not have
- * correct number of elements as that of $key.
- */
- public static function multisort(&$array, $key, $descending = false, $sortFlag = SORT_REGULAR, $caseSensitive = true)
- {
- $keys = is_array($key) ? $key : array($key);
- if (empty($keys) || empty($array)) {
- return;
- }
- $n = count($keys);
- if (is_scalar($descending)) {
- $descending = array_fill(0, $n, $descending);
- } elseif (count($descending) !== $n) {
- throw new InvalidParamException('The length of $descending parameter must be the same as that of $keys.');
- }
- if (is_scalar($sortFlag)) {
- $sortFlag = array_fill(0, $n, $sortFlag);
- } elseif (count($sortFlag) !== $n) {
- throw new InvalidParamException('The length of $sortFlag parameter must be the same as that of $keys.');
- }
- if (is_scalar($caseSensitive)) {
- $caseSensitive = array_fill(0, $n, $caseSensitive);
- } elseif (count($caseSensitive) !== $n) {
- throw new InvalidParamException('The length of $caseSensitive parameter must be the same as that of $keys.');
- }
- $args = array();
- foreach ($keys as $i => $key) {
- $flag = $sortFlag[$i];
- $cs = $caseSensitive[$i];
- if (!$cs && ($flag === SORT_STRING)) {
- if (defined('SORT_FLAG_CASE')) {
- $flag = $flag | SORT_FLAG_CASE;
- $args[] = static::getColumn($array, $key);
- } else {
- $column = array();
- foreach (static::getColumn($array, $key) as $k => $value) {
- $column[$k] = mb_strtolower($value);
- }
- $args[] = $column;
- }
- } else {
- $args[] = static::getColumn($array, $key);
- }
- $args[] = $descending[$i] ? SORT_DESC : SORT_ASC;
- $args[] = $flag;
- }
- $args[] = &$array;
- call_user_func_array('array_multisort', $args);
- }
-
- /**
- * Encodes special characters in an array of strings into HTML entities.
- * Both the array keys and values will be encoded.
- * If a value is an array, this method will also encode it recursively.
- * @param array $data data to be encoded
- * @param boolean $valuesOnly whether to encode array values only. If false,
- * both the array keys and array values will be encoded.
- * @param string $charset the charset that the data is using. If not set,
- * [[\yii\base\Application::charset]] will be used.
- * @return array the encoded data
- * @see http://www.php.net/manual/en/function.htmlspecialchars.php
- */
- public static function htmlEncode($data, $valuesOnly = true, $charset = null)
- {
- if ($charset === null) {
- $charset = Yii::$app->charset;
- }
- $d = array();
- foreach ($data as $key => $value) {
- if (!$valuesOnly && is_string($key)) {
- $key = htmlspecialchars($key, ENT_QUOTES, $charset);
- }
- if (is_string($value)) {
- $d[$key] = htmlspecialchars($value, ENT_QUOTES, $charset);
- } elseif (is_array($value)) {
- $d[$key] = static::htmlEncode($value, $charset);
- }
- }
- return $d;
- }
-
- /**
- * Decodes HTML entities into the corresponding characters in an array of strings.
- * Both the array keys and values will be decoded.
- * If a value is an array, this method will also decode it recursively.
- * @param array $data data to be decoded
- * @param boolean $valuesOnly whether to decode array values only. If false,
- * both the array keys and array values will be decoded.
- * @return array the decoded data
- * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
- */
- public static function htmlDecode($data, $valuesOnly = true)
- {
- $d = array();
- foreach ($data as $key => $value) {
- if (!$valuesOnly && is_string($key)) {
- $key = htmlspecialchars_decode($key, ENT_QUOTES);
- }
- if (is_string($value)) {
- $d[$key] = htmlspecialchars_decode($value, ENT_QUOTES);
- } elseif (is_array($value)) {
- $d[$key] = static::htmlDecode($value);
- }
- }
- return $d;
- }
-}
diff --git a/framework/yii/helpers/base/Console.php b/framework/yii/helpers/base/Console.php
deleted file mode 100644
index e3acbd9..0000000
--- a/framework/yii/helpers/base/Console.php
+++ /dev/null
@@ -1,834 +0,0 @@
-
- * @since 2.0
- */
-class Console
-{
- const FG_BLACK = 30;
- const FG_RED = 31;
- const FG_GREEN = 32;
- const FG_YELLOW = 33;
- const FG_BLUE = 34;
- const FG_PURPLE = 35;
- const FG_CYAN = 36;
- const FG_GREY = 37;
-
- const BG_BLACK = 40;
- const BG_RED = 41;
- const BG_GREEN = 42;
- const BG_YELLOW = 43;
- const BG_BLUE = 44;
- const BG_PURPLE = 45;
- const BG_CYAN = 46;
- const BG_GREY = 47;
-
- const RESET = 0;
- const NORMAL = 0;
- const BOLD = 1;
- const ITALIC = 3;
- const UNDERLINE = 4;
- const BLINK = 5;
- const NEGATIVE = 7;
- const CONCEALED = 8;
- const CROSSED_OUT = 9;
- const FRAMED = 51;
- const ENCIRCLED = 52;
- const OVERLINED = 53;
-
- /**
- * Moves the terminal cursor up by sending ANSI control code CUU to the terminal.
- * If the cursor is already at the edge of the screen, this has no effect.
- * @param integer $rows number of rows the cursor should be moved up
- */
- public static function moveCursorUp($rows = 1)
- {
- echo "\033[" . (int)$rows . 'A';
- }
-
- /**
- * Moves the terminal cursor down by sending ANSI control code CUD to the terminal.
- * If the cursor is already at the edge of the screen, this has no effect.
- * @param integer $rows number of rows the cursor should be moved down
- */
- public static function moveCursorDown($rows = 1)
- {
- echo "\033[" . (int)$rows . 'B';
- }
-
- /**
- * Moves the terminal cursor forward by sending ANSI control code CUF to the terminal.
- * If the cursor is already at the edge of the screen, this has no effect.
- * @param integer $steps number of steps the cursor should be moved forward
- */
- public static function moveCursorForward($steps = 1)
- {
- echo "\033[" . (int)$steps . 'C';
- }
-
- /**
- * Moves the terminal cursor backward by sending ANSI control code CUB to the terminal.
- * If the cursor is already at the edge of the screen, this has no effect.
- * @param integer $steps number of steps the cursor should be moved backward
- */
- public static function moveCursorBackward($steps = 1)
- {
- echo "\033[" . (int)$steps . 'D';
- }
-
- /**
- * Moves the terminal cursor to the beginning of the next line by sending ANSI control code CNL to the terminal.
- * @param integer $lines number of lines the cursor should be moved down
- */
- public static function moveCursorNextLine($lines = 1)
- {
- echo "\033[" . (int)$lines . 'E';
- }
-
- /**
- * Moves the terminal cursor to the beginning of the previous line by sending ANSI control code CPL to the terminal.
- * @param integer $lines number of lines the cursor should be moved up
- */
- public static function moveCursorPrevLine($lines = 1)
- {
- echo "\033[" . (int)$lines . 'F';
- }
-
- /**
- * Moves the cursor to an absolute position given as column and row by sending ANSI control code CUP or CHA to the terminal.
- * @param integer $column 1-based column number, 1 is the left edge of the screen.
- * @param integer|null $row 1-based row number, 1 is the top edge of the screen. if not set, will move cursor only in current line.
- */
- public static function moveCursorTo($column, $row = null)
- {
- if ($row === null) {
- echo "\033[" . (int)$column . 'G';
- } else {
- echo "\033[" . (int)$row . ';' . (int)$column . 'H';
- }
- }
-
- /**
- * Scrolls whole page up by sending ANSI control code SU to the terminal.
- * New lines are added at the bottom. This is not supported by ANSI.SYS used in windows.
- * @param int $lines number of lines to scroll up
- */
- public static function scrollUp($lines = 1)
- {
- echo "\033[" . (int)$lines . "S";
- }
-
- /**
- * Scrolls whole page down by sending ANSI control code SD to the terminal.
- * New lines are added at the top. This is not supported by ANSI.SYS used in windows.
- * @param int $lines number of lines to scroll down
- */
- public static function scrollDown($lines = 1)
- {
- echo "\033[" . (int)$lines . "T";
- }
-
- /**
- * Saves the current cursor position by sending ANSI control code SCP to the terminal.
- * Position can then be restored with {@link restoreCursorPosition}.
- */
- public static function saveCursorPosition()
- {
- echo "\033[s";
- }
-
- /**
- * Restores the cursor position saved with {@link saveCursorPosition} by sending ANSI control code RCP to the terminal.
- */
- public static function restoreCursorPosition()
- {
- echo "\033[u";
- }
-
- /**
- * Hides the cursor by sending ANSI DECTCEM code ?25l to the terminal.
- * Use {@link showCursor} to bring it back.
- * Do not forget to show cursor when your application exits. Cursor might stay hidden in terminal after exit.
- */
- public static function hideCursor()
- {
- echo "\033[?25l";
- }
-
- /**
- * Will show a cursor again when it has been hidden by {@link hideCursor} by sending ANSI DECTCEM code ?25h to the terminal.
- */
- public static function showCursor()
- {
- echo "\033[?25h";
- }
-
- /**
- * Clears entire screen content by sending ANSI control code ED with argument 2 to the terminal.
- * Cursor position will not be changed.
- * **Note:** ANSI.SYS implementation used in windows will reset cursor position to upper left corner of the screen.
- */
- public static function clearScreen()
- {
- echo "\033[2J";
- }
-
- /**
- * Clears text from cursor to the beginning of the screen by sending ANSI control code ED with argument 1 to the terminal.
- * Cursor position will not be changed.
- */
- public static function clearScreenBeforeCursor()
- {
- echo "\033[1J";
- }
-
- /**
- * Clears text from cursor to the end of the screen by sending ANSI control code ED with argument 0 to the terminal.
- * Cursor position will not be changed.
- */
- public static function clearScreenAfterCursor()
- {
- echo "\033[0J";
- }
-
- /**
- * Clears the line, the cursor is currently on by sending ANSI control code EL with argument 2 to the terminal.
- * Cursor position will not be changed.
- */
- public static function clearLine()
- {
- echo "\033[2K";
- }
-
- /**
- * Clears text from cursor position to the beginning of the line by sending ANSI control code EL with argument 1 to the terminal.
- * Cursor position will not be changed.
- */
- public static function clearLineBeforeCursor()
- {
- echo "\033[1K";
- }
-
- /**
- * Clears text from cursor position to the end of the line by sending ANSI control code EL with argument 0 to the terminal.
- * Cursor position will not be changed.
- */
- public static function clearLineAfterCursor()
- {
- echo "\033[0K";
- }
-
- /**
- * Returns the ANSI format code.
- *
- * @param array $format You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]].
- * TODO: documentation
- * @return string
- */
- public static function ansiFormatCode($format)
- {
- return "\033[" . implode(';', $format) . 'm';
- }
-
- /**
- * Sets the ANSI format for any text that is printed afterwards.
- *
- * @param array $format You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]].
- * TODO: documentation
- * @see ansiFormatEnd()
- */
- public static function beginAnsiFormat($format)
- {
- echo "\033[" . implode(';', $format) . 'm';
- }
-
- /**
- * Resets any ANSI format set by previous method [[ansiFormatBegin()]]
- * Any output after this is will have default text style.
- * This is equal to
- *
- * ```php
- * echo Console::ansiFormatCode(array(Console::RESET))
- * ```
- */
- public static function endAnsiFormat()
- {
- echo "\033[0m";
- }
-
- /**
- * Will return a string formatted with the given ANSI style
- *
- * @param string $string the string to be formatted
- * @param array $format array containing formatting values.
- * You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]].
- * @return string
- */
- public static function ansiFormat($string, $format = array())
- {
- $code = implode(';', $format);
- return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m";
- }
-
- /**
- * Returns the ansi format code for xterm foreground color.
- * You can pass the returnvalue of this to one of the formatting methods:
- * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]]
- *
- * @param integer $colorCode xterm color code
- * @return string
- * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors
- */
- public static function xtermFgColor($colorCode)
- {
- return '38;5;' . $colorCode;
- }
-
- /**
- * Returns the ansi format code for xterm foreground color.
- * You can pass the returnvalue of this to one of the formatting methods:
- * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]]
- *
- * @param integer $colorCode xterm color code
- * @return string
- * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors
- */
- public static function xtermBgColor($colorCode)
- {
- return '48;5;' . $colorCode;
- }
-
- /**
- * Strips ANSI control codes from a string
- *
- * @param string $string String to strip
- * @return string
- */
- public static function stripAnsiFormat($string)
- {
- return preg_replace('/\033\[[\d;?]*\w/', '', $string);
- }
-
- // TODO refactor and review
- public static function ansiToHtml($string)
- {
- $tags = 0;
- return preg_replace_callback(
- '/\033\[[\d;]+m/',
- function ($ansi) use (&$tags) {
- $styleA = array();
- foreach (explode(';', $ansi) as $controlCode) {
- switch ($controlCode) {
- case self::FG_BLACK:
- $style = array('color' => '#000000');
- break;
- case self::FG_BLUE:
- $style = array('color' => '#000078');
- break;
- case self::FG_CYAN:
- $style = array('color' => '#007878');
- break;
- case self::FG_GREEN:
- $style = array('color' => '#007800');
- break;
- case self::FG_GREY:
- $style = array('color' => '#787878');
- break;
- case self::FG_PURPLE:
- $style = array('color' => '#780078');
- break;
- case self::FG_RED:
- $style = array('color' => '#780000');
- break;
- case self::FG_YELLOW:
- $style = array('color' => '#787800');
- break;
- case self::BG_BLACK:
- $style = array('background-color' => '#000000');
- break;
- case self::BG_BLUE:
- $style = array('background-color' => '#000078');
- break;
- case self::BG_CYAN:
- $style = array('background-color' => '#007878');
- break;
- case self::BG_GREEN:
- $style = array('background-color' => '#007800');
- break;
- case self::BG_GREY:
- $style = array('background-color' => '#787878');
- break;
- case self::BG_PURPLE:
- $style = array('background-color' => '#780078');
- break;
- case self::BG_RED:
- $style = array('background-color' => '#780000');
- break;
- case self::BG_YELLOW:
- $style = array('background-color' => '#787800');
- break;
- case self::BOLD:
- $style = array('font-weight' => 'bold');
- break;
- case self::ITALIC:
- $style = array('font-style' => 'italic');
- break;
- case self::UNDERLINE:
- $style = array('text-decoration' => array('underline'));
- break;
- case self::OVERLINED:
- $style = array('text-decoration' => array('overline'));
- break;
- case self::CROSSED_OUT:
- $style = array('text-decoration' => array('line-through'));
- break;
- case self::BLINK:
- $style = array('text-decoration' => array('blink'));
- break;
- case self::NEGATIVE: // ???
- case self::CONCEALED:
- case self::ENCIRCLED:
- case self::FRAMED:
- // TODO allow resetting codes
- break;
- case 0: // ansi reset
- $return = '';
- for ($n = $tags; $tags > 0; $tags--) {
- $return .= ' ';
- }
- return $return;
- }
-
- $styleA = ArrayHelper::merge($styleA, $style);
- }
- $styleString[] = array();
- foreach ($styleA as $name => $content) {
- if ($name === 'text-decoration') {
- $content = implode(' ', $content);
- }
- $styleString[] = $name . ':' . $content;
- }
- $tags++;
- return '';
- },
- $string
- );
- }
-
- public function markdownToAnsi()
- {
- // TODO implement
- }
-
- /**
- * Converts a string to ansi formatted by replacing patterns like %y (for yellow) with ansi control codes
- *
- * // TODO documentation
- * Uses almost the same syntax as https://github.com/pear/Console_Color2/blob/master/Console/Color2.php
- * The conversion table is: ('bold' meaning 'light' on some
- * terminals). It's almost the same conversion table irssi uses.
- *
- * text text background
- * ------------------------------------------------
- * %k %K %0 black dark grey black
- * %r %R %1 red bold red red
- * %g %G %2 green bold green green
- * %y %Y %3 yellow bold yellow yellow
- * %b %B %4 blue bold blue blue
- * %m %M %5 magenta bold magenta magenta
- * %p %P magenta (think: purple)
- * %c %C %6 cyan bold cyan cyan
- * %w %W %7 white bold white white
- *
- * %F Blinking, Flashing
- * %U Underline
- * %8 Reverse
- * %_,%9 Bold
- *
- * %n Resets the color
- * %% A single %
- *
- * First param is the string to convert, second is an optional flag if
- * colors should be used. It defaults to true, if set to false, the
- * colorcodes will just be removed (And %% will be transformed into %)
- *
- * @param string $string String to convert
- * @param bool $colored Should the string be colored?
- * @return string
- */
- public static function renderColoredString($string, $colored = true)
- {
- static $conversions = array(
- '%y' => array(self::FG_YELLOW),
- '%g' => array(self::FG_GREEN),
- '%b' => array(self::FG_BLUE),
- '%r' => array(self::FG_RED),
- '%p' => array(self::FG_PURPLE),
- '%m' => array(self::FG_PURPLE),
- '%c' => array(self::FG_CYAN),
- '%w' => array(self::FG_GREY),
- '%k' => array(self::FG_BLACK),
- '%n' => array(0), // reset
- '%Y' => array(self::FG_YELLOW, self::BOLD),
- '%G' => array(self::FG_GREEN, self::BOLD),
- '%B' => array(self::FG_BLUE, self::BOLD),
- '%R' => array(self::FG_RED, self::BOLD),
- '%P' => array(self::FG_PURPLE, self::BOLD),
- '%M' => array(self::FG_PURPLE, self::BOLD),
- '%C' => array(self::FG_CYAN, self::BOLD),
- '%W' => array(self::FG_GREY, self::BOLD),
- '%K' => array(self::FG_BLACK, self::BOLD),
- '%N' => array(0, self::BOLD),
- '%3' => array(self::BG_YELLOW),
- '%2' => array(self::BG_GREEN),
- '%4' => array(self::BG_BLUE),
- '%1' => array(self::BG_RED),
- '%5' => array(self::BG_PURPLE),
- '%6' => array(self::BG_PURPLE),
- '%7' => array(self::BG_CYAN),
- '%0' => array(self::BG_GREY),
- '%F' => array(self::BLINK),
- '%U' => array(self::UNDERLINE),
- '%8' => array(self::NEGATIVE),
- '%9' => array(self::BOLD),
- '%_' => array(self::BOLD)
- );
-
- if ($colored) {
- $string = str_replace('%%', '% ', $string);
- foreach ($conversions as $key => $value) {
- $string = str_replace(
- $key,
- static::ansiFormatCode($value),
- $string
- );
- }
- $string = str_replace('% ', '%', $string);
- } else {
- $string = preg_replace('/%((%)|.)/', '$2', $string);
- }
- return $string;
- }
-
- /**
- * Escapes % so they don't get interpreted as color codes when
- * the string is parsed by [[renderColoredString]]
- *
- * @param string $string String to escape
- *
- * @access public
- * @return string
- */
- public static function escape($string)
- {
- return str_replace('%', '%%', $string);
- }
-
- /**
- * Returns true if the stream supports colorization. ANSI colors are disabled if not supported by the stream.
- *
- * - windows without ansicon
- * - not tty consoles
- *
- * @param mixed $stream
- * @return bool true if the stream supports ANSI colors, otherwise false.
- */
- public static function streamSupportsAnsiColors($stream)
- {
- return DIRECTORY_SEPARATOR == '\\'
- ? getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON'
- : function_exists('posix_isatty') && @posix_isatty($stream);
- }
-
- /**
- * Returns true if the console is running on windows
- * @return bool
- */
- public static function isRunningOnWindows()
- {
- return DIRECTORY_SEPARATOR == '\\';
- }
-
- /**
- * Usage: list($w, $h) = ConsoleHelper::getScreenSize();
- *
- * @param bool $refresh whether to force checking and not re-use cached size value.
- * This is useful to detect changing window size while the application is running but may
- * not get up to date values on every terminal.
- * @return array|boolean An array of ($width, $height) or false when it was not able to determine size.
- */
- public static function getScreenSize($refresh = false)
- {
- static $size;
- if ($size !== null && !$refresh) {
- return $size;
- }
-
- if (static::isRunningOnWindows()) {
- $output = array();
- exec('mode con', $output);
- if (isset($output) && strpos($output[1], 'CON') !== false) {
- return $size = array((int)preg_replace('~[^0-9]~', '', $output[3]), (int)preg_replace('~[^0-9]~', '', $output[4]));
- }
- } else {
- // try stty if available
- $stty = array();
- if (exec('stty -a 2>&1', $stty) && preg_match('/rows\s+(\d+);\s*columns\s+(\d+);/mi', implode(' ', $stty), $matches)) {
- return $size = array($matches[2], $matches[1]);
- }
-
- // fallback to tput, which may not be updated on terminal resize
- if (($width = (int) exec('tput cols 2>&1')) > 0 && ($height = (int) exec('tput lines 2>&1')) > 0) {
- return $size = array($width, $height);
- }
-
- // fallback to ENV variables, which may not be updated on terminal resize
- if (($width = (int) getenv('COLUMNS')) > 0 && ($height = (int) getenv('LINES')) > 0) {
- return $size = array($width, $height);
- }
- }
-
- return $size = false;
- }
-
- /**
- * Gets input from STDIN and returns a string right-trimmed for EOLs.
- *
- * @param bool $raw If set to true, returns the raw string without trimming
- * @return string
- */
- public static function stdin($raw = false)
- {
- return $raw ? fgets(STDIN) : rtrim(fgets(STDIN), PHP_EOL);
- }
-
- /**
- * Prints a string to STDOUT.
- *
- * @param string $string the string to print
- * @return int|boolean Number of bytes printed or false on error
- */
- public static function stdout($string)
- {
- return fwrite(STDOUT, $string);
- }
-
- /**
- * Prints a string to STDERR.
- *
- * @param string $string the string to print
- * @return int|boolean Number of bytes printed or false on error
- */
- public static function stderr($string)
- {
- return fwrite(STDERR, $string);
- }
-
- /**
- * Asks the user for input. Ends when the user types a carriage return (PHP_EOL). Optionally, It also provides a
- * prompt.
- *
- * @param string $prompt the prompt (optional)
- * @return string the user's input
- */
- public static function input($prompt = null)
- {
- if (isset($prompt)) {
- static::stdout($prompt);
- }
- return static::stdin();
- }
-
- /**
- * Prints text to STDOUT appended with a carriage return (PHP_EOL).
- *
- * @param string $string
- * @return mixed Number of bytes printed or bool false on error
- */
- public static function output($string = null)
- {
- return static::stdout($string . PHP_EOL);
- }
-
- /**
- * Prints text to STDERR appended with a carriage return (PHP_EOL).
- *
- * @param string $string
- * @return mixed Number of bytes printed or false on error
- */
- public static function error($string = null)
- {
- return static::stderr($string . PHP_EOL);
- }
-
- /**
- * Prompts the user for input and validates it
- *
- * @param string $text prompt string
- * @param array $options the options to validate the input:
- * - required: whether it is required or not
- * - default: default value if no input is inserted by the user
- * - pattern: regular expression pattern to validate user input
- * - validator: a callable function to validate input. The function must accept two parameters:
- * - $input: the user input to validate
- * - $error: the error value passed by reference if validation failed.
- * @return string the user input
- */
- public static function prompt($text, $options = array())
- {
- $options = ArrayHelper::merge(
- $options,
- array(
- 'required' => false,
- 'default' => null,
- 'pattern' => null,
- 'validator' => null,
- 'error' => 'Invalid input.',
- )
- );
- $error = null;
-
- top:
- $input = $options['default']
- ? static::input("$text [" . $options['default'] . ']: ')
- : static::input("$text: ");
-
- if (!strlen($input)) {
- if (isset($options['default'])) {
- $input = $options['default'];
- } elseif ($options['required']) {
- static::output($options['error']);
- goto top;
- }
- } elseif ($options['pattern'] && !preg_match($options['pattern'], $input)) {
- static::output($options['error']);
- goto top;
- } elseif ($options['validator'] &&
- !call_user_func_array($options['validator'], array($input, &$error))
- ) {
- static::output(isset($error) ? $error : $options['error']);
- goto top;
- }
-
- return $input;
- }
-
- /**
- * Asks user to confirm by typing y or n.
- *
- * @param string $message to echo out before waiting for user input
- * @param boolean $default this value is returned if no selection is made.
- * @return boolean whether user confirmed
- */
- public static function confirm($message, $default = true)
- {
- echo $message . ' (yes|no) [' . ($default ? 'yes' : 'no') . ']:';
- $input = trim(static::stdin());
- return empty($input) ? $default : !strncasecmp($input, 'y', 1);
- }
-
- /**
- * Gives the user an option to choose from. Giving '?' as an input will show
- * a list of options to choose from and their explanations.
- *
- * @param string $prompt the prompt message
- * @param array $options Key-value array of options to choose from
- *
- * @return string An option character the user chose
- */
- public static function select($prompt, $options = array())
- {
- top:
- static::stdout("$prompt [" . implode(',', array_keys($options)) . ",?]: ");
- $input = static::stdin();
- if ($input === '?') {
- foreach ($options as $key => $value) {
- echo " $key - $value\n";
- }
- echo " ? - Show help\n";
- goto top;
- } elseif (!in_array($input, array_keys($options))) {
- goto top;
- }
- return $input;
- }
-
- /**
- * Displays and updates a simple progress bar on screen.
- *
- * @param integer $done the number of items that are completed
- * @param integer $total the total value of items that are to be done
- * @param integer $size the size of the status bar (optional)
- * @see http://snipplr.com/view/29548/
- */
- public static function showProgress($done, $total, $size = 30)
- {
- static $start;
-
- // if we go over our bound, just ignore it
- if ($done > $total) {
- return;
- }
-
- if (empty($start)) {
- $start = time();
- }
-
- $now = time();
-
- $percent = (double)($done / $total);
- $bar = floor($percent * $size);
-
- $status = "\r[";
- $status .= str_repeat("=", $bar);
- if ($bar < $size) {
- $status .= ">";
- $status .= str_repeat(" ", $size - $bar);
- } else {
- $status .= "=";
- }
-
- $display = number_format($percent * 100, 0);
-
- $status .= "] $display% $done/$total";
-
- $rate = ($now - $start) / $done;
- $left = $total - $done;
- $eta = round($rate * $left, 2);
-
- $elapsed = $now - $start;
-
- $status .= " remaining: " . number_format($eta) . " sec. elapsed: " . number_format($elapsed) . " sec.";
-
- static::stdout("$status ");
-
- flush();
-
- // when done, send a newline
- if ($done == $total) {
- echo "\n";
- }
- }
-}
diff --git a/framework/yii/helpers/base/FileHelper.php b/framework/yii/helpers/base/FileHelper.php
deleted file mode 100644
index 954c86e..0000000
--- a/framework/yii/helpers/base/FileHelper.php
+++ /dev/null
@@ -1,172 +0,0 @@
-
- * @author Alex Makarov
- * @since 2.0
- */
-class FileHelper
-{
- /**
- * Normalizes a file/directory path.
- * After normalization, the directory separators in the path will be `DIRECTORY_SEPARATOR`,
- * and any trailing directory separators will be removed. For example, '/home\demo/' on Linux
- * will be normalized as '/home/demo'.
- * @param string $path the file/directory path to be normalized
- * @param string $ds the directory separator to be used in the normalized result. Defaults to `DIRECTORY_SEPARATOR`.
- * @return string the normalized file/directory path
- */
- public static function normalizePath($path, $ds = DIRECTORY_SEPARATOR)
- {
- return rtrim(strtr($path, array('/' => $ds, '\\' => $ds)), $ds);
- }
-
- /**
- * Returns the localized version of a specified file.
- *
- * The searching is based on the specified language code. In particular,
- * a file with the same name will be looked for under the subdirectory
- * whose name is the same as the language code. For example, given the file "path/to/view.php"
- * and language code "zh_CN", the localized file will be looked for as
- * "path/to/zh_CN/view.php". If the file is not found, the original file
- * will be returned.
- *
- * If the target and the source language codes are the same,
- * the original file will be returned.
- *
- * @param string $file the original file
- * @param string $language the target language that the file should be localized to.
- * If not set, the value of [[\yii\base\Application::language]] will be used.
- * @param string $sourceLanguage the language that the original file is in.
- * If not set, the value of [[\yii\base\Application::sourceLanguage]] will be used.
- * @return string the matching localized file, or the original file if the localized version is not found.
- * If the target and the source language codes are the same, the original file will be returned.
- */
- public static function localize($file, $language = null, $sourceLanguage = null)
- {
- if ($language === null) {
- $language = Yii::$app->language;
- }
- if ($sourceLanguage === null) {
- $sourceLanguage = Yii::$app->sourceLanguage;
- }
- if ($language === $sourceLanguage) {
- return $file;
- }
- $desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $sourceLanguage . DIRECTORY_SEPARATOR . basename($file);
- return is_file($desiredFile) ? $desiredFile : $file;
- }
-
- /**
- * Determines the MIME type of the specified file.
- * This method will first try to determine the MIME type based on
- * [finfo_open](http://php.net/manual/en/function.finfo-open.php). If this doesn't work, it will
- * fall back to [[getMimeTypeByExtension()]].
- * @param string $file the file name.
- * @param string $magicFile name of the optional magic database file, usually something like `/path/to/magic.mime`.
- * This will be passed as the second parameter to [finfo_open](http://php.net/manual/en/function.finfo-open.php).
- * @param boolean $checkExtension whether to use the file extension to determine the MIME type in case
- * `finfo_open()` cannot determine it.
- * @return string the MIME type (e.g. `text/plain`). Null is returned if the MIME type cannot be determined.
- */
- public static function getMimeType($file, $magicFile = null, $checkExtension = true)
- {
- if (function_exists('finfo_open')) {
- $info = finfo_open(FILEINFO_MIME_TYPE, $magicFile);
- if ($info) {
- $result = finfo_file($info, $file);
- finfo_close($info);
- if ($result !== false) {
- return $result;
- }
- }
- }
-
- return $checkExtension ? self::getMimeTypeByExtension($file) : null;
- }
-
- /**
- * Determines the MIME type based on the extension name of the specified file.
- * This method will use a local map between extension names and MIME types.
- * @param string $file the file name.
- * @param string $magicFile the path of the file that contains all available MIME type information.
- * If this is not set, the default file aliased by `@yii/util/mimeTypes.php` will be used.
- * @return string the MIME type. Null is returned if the MIME type cannot be determined.
- */
- public static function getMimeTypeByExtension($file, $magicFile = null)
- {
- static $mimeTypes = array();
- if ($magicFile === null) {
- $magicFile = __DIR__ . '/mimeTypes.php';
- }
- if (!isset($mimeTypes[$magicFile])) {
- $mimeTypes[$magicFile] = require($magicFile);
- }
- if (($ext = pathinfo($file, PATHINFO_EXTENSION)) !== '') {
- $ext = strtolower($ext);
- if (isset($mimeTypes[$magicFile][$ext])) {
- return $mimeTypes[$magicFile][$ext];
- }
- }
- return null;
- }
-
- /**
- * Copies a whole directory as another one.
- * The files and sub-directories will also be copied over.
- * @param string $src the source directory
- * @param string $dst the destination directory
- * @param array $options options for directory copy. Valid options are:
- *
- * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0777.
- * - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting.
- * - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.
- * If the callback returns false, the copy operation for the sub-directory or file will be cancelled.
- * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or
- * file to be copied from, while `$to` is the copy target.
- * - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied.
- * The signature of the callback is similar to that of `beforeCopy`.
- */
- public static function copyDirectory($src, $dst, $options = array())
- {
- if (!is_dir($dst)) {
- mkdir($dst, isset($options['dirMode']) ? $options['dirMode'] : 0777, true);
- }
-
- $handle = opendir($src);
- while (($file = readdir($handle)) !== false) {
- if ($file === '.' || $file === '..') {
- continue;
- }
- $from = $src . DIRECTORY_SEPARATOR . $file;
- $to = $dst . DIRECTORY_SEPARATOR . $file;
- if (!isset($options['beforeCopy']) || call_user_func($options['beforeCopy'], $from, $to)) {
- if (is_file($from)) {
- copy($from, $to);
- if (isset($options['fileMode'])) {
- @chmod($to, $options['fileMode']);
- }
- } else {
- static::copyDirectory($from, $to, $options);
- }
- if (isset($options['afterCopy'])) {
- call_user_func($options['afterCopy'], $from, $to);
- }
- }
- }
- closedir($handle);
- }
-}
diff --git a/framework/yii/helpers/base/Html.php b/framework/yii/helpers/base/Html.php
deleted file mode 100644
index 47385e2..0000000
--- a/framework/yii/helpers/base/Html.php
+++ /dev/null
@@ -1,1482 +0,0 @@
-
- * @since 2.0
- */
-class Html
-{
- /**
- * @var boolean whether to close void (empty) elements. Defaults to true.
- * @see voidElements
- */
- public static $closeVoidElements = true;
- /**
- * @var array list of void elements (element name => 1)
- * @see closeVoidElements
- * @see http://www.w3.org/TR/html-markup/syntax.html#void-element
- */
- public static $voidElements = array(
- 'area' => 1,
- 'base' => 1,
- 'br' => 1,
- 'col' => 1,
- 'command' => 1,
- 'embed' => 1,
- 'hr' => 1,
- 'img' => 1,
- 'input' => 1,
- 'keygen' => 1,
- 'link' => 1,
- 'meta' => 1,
- 'param' => 1,
- 'source' => 1,
- 'track' => 1,
- 'wbr' => 1,
- );
- /**
- * @var boolean whether to show the values of boolean attributes in element tags.
- * If false, only the attribute names will be generated.
- * @see booleanAttributes
- */
- public static $showBooleanAttributeValues = true;
- /**
- * @var array list of boolean attributes. The presence of a boolean attribute on
- * an element represents the true value, and the absence of the attribute represents the false value.
- * @see showBooleanAttributeValues
- * @see http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes
- */
- public static $booleanAttributes = array(
- 'async' => 1,
- 'autofocus' => 1,
- 'autoplay' => 1,
- 'checked' => 1,
- 'controls' => 1,
- 'declare' => 1,
- 'default' => 1,
- 'defer' => 1,
- 'disabled' => 1,
- 'formnovalidate' => 1,
- 'hidden' => 1,
- 'ismap' => 1,
- 'loop' => 1,
- 'multiple' => 1,
- 'muted' => 1,
- 'nohref' => 1,
- 'noresize' => 1,
- 'novalidate' => 1,
- 'open' => 1,
- 'readonly' => 1,
- 'required' => 1,
- 'reversed' => 1,
- 'scoped' => 1,
- 'seamless' => 1,
- 'selected' => 1,
- 'typemustmatch' => 1,
- );
- /**
- * @var array the preferred order of attributes in a tag. This mainly affects the order of the attributes
- * that are rendered by [[renderAttributes()]].
- */
- public static $attributeOrder = array(
- 'type',
- 'id',
- 'class',
- 'name',
- 'value',
-
- 'href',
- 'src',
- 'action',
- 'method',
-
- 'selected',
- 'checked',
- 'readonly',
- 'disabled',
- 'multiple',
-
- 'size',
- 'maxlength',
- 'width',
- 'height',
- 'rows',
- 'cols',
-
- 'alt',
- 'title',
- 'rel',
- 'media',
- );
-
- /**
- * Encodes special characters into HTML entities.
- * The [[yii\base\Application::charset|application charset]] will be used for encoding.
- * @param string $content the content to be encoded
- * @param boolean $doubleEncode whether to encode HTML entities in `$content`. If false,
- * HTML entities in `$content` will not be further encoded.
- * @return string the encoded content
- * @see decode
- * @see http://www.php.net/manual/en/function.htmlspecialchars.php
- */
- public static function encode($content, $doubleEncode = true)
- {
- return htmlspecialchars($content, ENT_QUOTES, Yii::$app->charset, $doubleEncode);
- }
-
- /**
- * Decodes special HTML entities back to the corresponding characters.
- * This is the opposite of [[encode()]].
- * @param string $content the content to be decoded
- * @return string the decoded content
- * @see encode
- * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
- */
- public static function decode($content)
- {
- return htmlspecialchars_decode($content, ENT_QUOTES);
- }
-
- /**
- * Generates a complete HTML tag.
- * @param string $name the tag name
- * @param string $content the content to be enclosed between the start and end tags. It will not be HTML-encoded.
- * If this is coming from end users, you should consider [[encode()]] it to prevent XSS attacks.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated HTML tag
- * @see beginTag
- * @see endTag
- */
- public static function tag($name, $content = '', $options = array())
- {
- $html = '<' . $name . static::renderTagAttributes($options);
- if (isset(static::$voidElements[strtolower($name)])) {
- return $html . (static::$closeVoidElements ? ' />' : '>');
- } else {
- return $html . ">$content$name>";
- }
- }
-
- /**
- * Generates a start tag.
- * @param string $name the tag name
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated start tag
- * @see endTag
- * @see tag
- */
- public static function beginTag($name, $options = array())
- {
- return '<' . $name . static::renderTagAttributes($options) . '>';
- }
-
- /**
- * Generates an end tag.
- * @param string $name the tag name
- * @return string the generated end tag
- * @see beginTag
- * @see tag
- */
- public static function endTag($name)
- {
- return "$name>";
- }
-
- /**
- * Encloses the given content within a CDATA tag.
- * @param string $content the content to be enclosed within the CDATA tag
- * @return string the CDATA tag with the enclosed content.
- */
- public static function cdata($content)
- {
- return '';
- }
-
- /**
- * Generates a style tag.
- * @param string $content the style content
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * If the options does not contain "type", a "type" attribute with value "text/css" will be used.
- * @return string the generated style tag
- */
- public static function style($content, $options = array())
- {
- if (!isset($options['type'])) {
- $options['type'] = 'text/css';
- }
- return static::tag('style', "/**/", $options);
- }
-
- /**
- * Generates a script tag.
- * @param string $content the script content
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * If the options does not contain "type", a "type" attribute with value "text/javascript" will be rendered.
- * @return string the generated script tag
- */
- public static function script($content, $options = array())
- {
- if (!isset($options['type'])) {
- $options['type'] = 'text/javascript';
- }
- return static::tag('script', "/**/", $options);
- }
-
- /**
- * Generates a link tag that refers to an external CSS file.
- * @param array|string $url the URL of the external CSS file. This parameter will be processed by [[url()]].
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated link tag
- * @see url
- */
- public static function cssFile($url, $options = array())
- {
- $options['rel'] = 'stylesheet';
- $options['type'] = 'text/css';
- $options['href'] = static::url($url);
- return static::tag('link', '', $options);
- }
-
- /**
- * Generates a script tag that refers to an external JavaScript file.
- * @param string $url the URL of the external JavaScript file. This parameter will be processed by [[url()]].
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated script tag
- * @see url
- */
- public static function jsFile($url, $options = array())
- {
- $options['type'] = 'text/javascript';
- $options['src'] = static::url($url);
- return static::tag('script', '', $options);
- }
-
- /**
- * Generates a form start tag.
- * @param array|string $action the form action URL. This parameter will be processed by [[url()]].
- * @param string $method the form submission method, such as "post", "get", "put", "delete" (case-insensitive).
- * Since most browsers only support "post" and "get", if other methods are given, they will
- * be simulated using "post", and a hidden input will be added which contains the actual method type.
- * See [[\yii\web\Request::restVar]] for more details.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated form start tag.
- * @see endForm
- */
- public static function beginForm($action = '', $method = 'post', $options = array())
- {
- $action = static::url($action);
-
- $hiddenInputs = array();
-
- $request = Yii::$app->getRequest();
- if ($request instanceof Request) {
- if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) {
- // simulate PUT, DELETE, etc. via POST
- $hiddenInputs[] = static::hiddenInput($request->restVar, $method);
- $method = 'post';
- }
- if ($request->enableCsrfValidation) {
- $hiddenInputs[] = static::hiddenInput($request->csrfTokenName, $request->getCsrfToken());
- }
- }
-
- if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) {
- // query parameters in the action are ignored for GET method
- // we use hidden fields to add them back
- foreach (explode('&', substr($action, $pos + 1)) as $pair) {
- if (($pos1 = strpos($pair, '=')) !== false) {
- $hiddenInputs[] = static::hiddenInput(urldecode(substr($pair, 0, $pos1)), urldecode(substr($pair, $pos1 + 1)));
- } else {
- $hiddenInputs[] = static::hiddenInput(urldecode($pair), '');
- }
- }
- $action = substr($action, 0, $pos);
- }
-
- $options['action'] = $action;
- $options['method'] = $method;
- $form = static::beginTag('form', $options);
- if (!empty($hiddenInputs)) {
- $form .= "\n" . implode("\n", $hiddenInputs);
- }
-
- return $form;
- }
-
- /**
- * Generates a form end tag.
- * @return string the generated tag
- * @see beginForm
- */
- public static function endForm()
- {
- return '';
- }
-
- /**
- * Generates a hyperlink tag.
- * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
- * such as an image tag. If this is coming from end users, you should consider [[encode()]]
- * it to prevent XSS attacks.
- * @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[url()]]
- * and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute
- * will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated hyperlink
- * @see url
- */
- public static function a($text, $url = null, $options = array())
- {
- if ($url !== null) {
- $options['href'] = static::url($url);
- }
- return static::tag('a', $text, $options);
- }
-
- /**
- * Generates a mailto hyperlink.
- * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
- * such as an image tag. If this is coming from end users, you should consider [[encode()]]
- * it to prevent XSS attacks.
- * @param string $email email address. If this is null, the first parameter (link body) will be treated
- * as the email address and used.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated mailto link
- */
- public static function mailto($text, $email = null, $options = array())
- {
- $options['href'] = 'mailto:' . ($email === null ? $text : $email);
- return static::tag('a', $text, $options);
- }
-
- /**
- * Generates an image tag.
- * @param string $src the image URL. This parameter will be processed by [[url()]].
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated image tag
- */
- public static function img($src, $options = array())
- {
- $options['src'] = static::url($src);
- if (!isset($options['alt'])) {
- $options['alt'] = '';
- }
- return static::tag('img', null, $options);
- }
-
- /**
- * Generates a label tag.
- * @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code
- * such as an image tag. If this is is coming from end users, you should [[encode()]]
- * it to prevent XSS attacks.
- * @param string $for the ID of the HTML element that this label is associated with.
- * If this is null, the "for" attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated label tag
- */
- public static function label($content, $for = null, $options = array())
- {
- $options['for'] = $for;
- return static::tag('label', $content, $options);
- }
-
- /**
- * Generates a button tag.
- * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
- * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
- * you should consider [[encode()]] it to prevent XSS attacks.
- * @param string $name the name attribute. If it is null, the name attribute will not be generated.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * If the options does not contain "type", a "type" attribute with value "button" will be rendered.
- * @return string the generated button tag
- */
- public static function button($content = 'Button', $name = null, $value = null, $options = array())
- {
- $options['name'] = $name;
- $options['value'] = $value;
- if (!isset($options['type'])) {
- $options['type'] = 'button';
- }
- return static::tag('button', $content, $options);
- }
-
- /**
- * Generates a submit button tag.
- * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
- * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
- * you should consider [[encode()]] it to prevent XSS attacks.
- * @param string $name the name attribute. If it is null, the name attribute will not be generated.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated submit button tag
- */
- public static function submitButton($content = 'Submit', $name = null, $value = null, $options = array())
- {
- $options['type'] = 'submit';
- return static::button($content, $name, $value, $options);
- }
-
- /**
- * Generates a reset button tag.
- * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
- * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
- * you should consider [[encode()]] it to prevent XSS attacks.
- * @param string $name the name attribute. If it is null, the name attribute will not be generated.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated reset button tag
- */
- public static function resetButton($content = 'Reset', $name = null, $value = null, $options = array())
- {
- $options['type'] = 'reset';
- return static::button($content, $name, $value, $options);
- }
-
- /**
- * Generates an input type of the given type.
- * @param string $type the type attribute.
- * @param string $name the name attribute. If it is null, the name attribute will not be generated.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated input tag
- */
- public static function input($type, $name = null, $value = null, $options = array())
- {
- $options['type'] = $type;
- $options['name'] = $name;
- $options['value'] = $value;
- return static::tag('input', null, $options);
- }
-
- /**
- * Generates an input button.
- * @param string $name the name attribute.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated button tag
- */
- public static function buttonInput($name, $value = 'Button', $options = array())
- {
- return static::input('button', $name, $value, $options);
- }
-
- /**
- * Generates a submit input button.
- * @param string $name the name attribute. If it is null, the name attribute will not be generated.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated button tag
- */
- public static function submitInput($name = null, $value = 'Submit', $options = array())
- {
- return static::input('submit', $name, $value, $options);
- }
-
- /**
- * Generates a reset input button.
- * @param string $name the name attribute. If it is null, the name attribute will not be generated.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]].
- * Attributes whose value is null will be ignored and not put in the tag returned.
- * @return string the generated button tag
- */
- public static function resetInput($name = null, $value = 'Reset', $options = array())
- {
- return static::input('reset', $name, $value, $options);
- }
-
- /**
- * Generates a text input field.
- * @param string $name the name attribute.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated button tag
- */
- public static function textInput($name, $value = null, $options = array())
- {
- return static::input('text', $name, $value, $options);
- }
-
- /**
- * Generates a hidden input field.
- * @param string $name the name attribute.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated button tag
- */
- public static function hiddenInput($name, $value = null, $options = array())
- {
- return static::input('hidden', $name, $value, $options);
- }
-
- /**
- * Generates a password input field.
- * @param string $name the name attribute.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated button tag
- */
- public static function passwordInput($name, $value = null, $options = array())
- {
- return static::input('password', $name, $value, $options);
- }
-
- /**
- * Generates a file input field.
- * To use a file input field, you should set the enclosing form's "enctype" attribute to
- * be "multipart/form-data". After the form is submitted, the uploaded file information
- * can be obtained via $_FILES[$name] (see PHP documentation).
- * @param string $name the name attribute.
- * @param string $value the value attribute. If it is null, the value attribute will not be generated.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated button tag
- */
- public static function fileInput($name, $value = null, $options = array())
- {
- return static::input('file', $name, $value, $options);
- }
-
- /**
- * Generates a text area input.
- * @param string $name the input name
- * @param string $value the input value. Note that it will be encoded using [[encode()]].
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * @return string the generated text area tag
- */
- public static function textarea($name, $value = '', $options = array())
- {
- $options['name'] = $name;
- return static::tag('textarea', static::encode($value), $options);
- }
-
- /**
- * Generates a radio button input.
- * @param string $name the name attribute.
- * @param boolean $checked whether the radio button should be checked.
- * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
- *
- * - uncheck: string, the value associated with the uncheck state of the radio button. When this attribute
- * is present, a hidden input will be generated so that if the radio button is not checked and is submitted,
- * the value of this attribute will still be submitted to the server via the hidden input.
- *
- * The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
- *
- * @return string the generated radio button tag
- */
- public static function radio($name, $checked = false, $options = array())
- {
- $options['checked'] = $checked;
- $value = array_key_exists('value', $options) ? $options['value'] : '1';
- if (isset($options['uncheck'])) {
- // add a hidden field so that if the radio button is not selected, it still submits a value
- $hidden = static::hiddenInput($name, $options['uncheck']);
- unset($options['uncheck']);
- } else {
- $hidden = '';
- }
- return $hidden . static::input('radio', $name, $value, $options);
- }
-
- /**
- * Generates a checkbox input.
- * @param string $name the name attribute.
- * @param boolean $checked whether the checkbox should be checked.
- * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
- *
- * - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute
- * is present, a hidden input will be generated so that if the checkbox is not checked and is submitted,
- * the value of this attribute will still be submitted to the server via the hidden input.
- *
- * The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
- *
- * @return string the generated checkbox tag
- */
- public static function checkbox($name, $checked = false, $options = array())
- {
- $options['checked'] = $checked;
- $value = array_key_exists('value', $options) ? $options['value'] : '1';
- if (isset($options['uncheck'])) {
- // add a hidden field so that if the checkbox is not selected, it still submits a value
- $hidden = static::hiddenInput($name, $options['uncheck']);
- unset($options['uncheck']);
- } else {
- $hidden = '';
- }
- return $hidden . static::input('checkbox', $name, $value, $options);
- }
-
- /**
- * Generates a drop-down list.
- * @param string $name the input name
- * @param string $selection the selected value
- * @param array $items the option data items. The array keys are option values, and the array values
- * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
- * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
- * If you have a list of data models, you may convert them into the format described above using
- * [[\yii\helpers\ArrayHelper::map()]].
- *
- * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
- * the labels will also be HTML-encoded.
- * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
- *
- * - prompt: string, a prompt text to be displayed as the first option;
- * - options: array, the attributes for the select option tags. The array keys must be valid option values,
- * and the array values are the extra attributes for the corresponding option tags. For example,
- *
- * ~~~
- * array(
- * 'value1' => array('disabled' => true),
- * 'value2' => array('label' => 'value 2'),
- * );
- * ~~~
- *
- * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
- * except that the array keys represent the optgroup labels specified in $items.
- *
- * The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
- *
- * @return string the generated drop-down list tag
- */
- public static function dropDownList($name, $selection = null, $items = array(), $options = array())
- {
- $options['name'] = $name;
- $selectOptions = static::renderSelectOptions($selection, $items, $options);
- return static::tag('select', "\n" . $selectOptions . "\n", $options);
- }
-
- /**
- * Generates a list box.
- * @param string $name the input name
- * @param string|array $selection the selected value(s)
- * @param array $items the option data items. The array keys are option values, and the array values
- * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
- * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
- * If you have a list of data models, you may convert them into the format described above using
- * [[\yii\helpers\ArrayHelper::map()]].
- *
- * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
- * the labels will also be HTML-encoded.
- * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
- *
- * - prompt: string, a prompt text to be displayed as the first option;
- * - options: array, the attributes for the select option tags. The array keys must be valid option values,
- * and the array values are the extra attributes for the corresponding option tags. For example,
- *
- * ~~~
- * array(
- * 'value1' => array('disabled' => true),
- * 'value2' => array('label' => 'value 2'),
- * );
- * ~~~
- *
- * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
- * except that the array keys represent the optgroup labels specified in $items.
- * - unselect: string, the value that will be submitted when no option is selected.
- * When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
- * mode, we can still obtain the posted unselect value.
- *
- * The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
- *
- * @return string the generated list box tag
- */
- public static function listBox($name, $selection = null, $items = array(), $options = array())
- {
- if (!isset($options['size'])) {
- $options['size'] = 4;
- }
- if (!empty($options['multiple']) && substr($name, -2) !== '[]') {
- $name .= '[]';
- }
- $options['name'] = $name;
- if (isset($options['unselect'])) {
- // add a hidden field so that if the list box has no option being selected, it still submits a value
- if (substr($name, -2) === '[]') {
- $name = substr($name, 0, -2);
- }
- $hidden = static::hiddenInput($name, $options['unselect']);
- unset($options['unselect']);
- } else {
- $hidden = '';
- }
- $selectOptions = static::renderSelectOptions($selection, $items, $options);
- return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options);
- }
-
- /**
- * Generates a list of checkboxes.
- * A checkbox list allows multiple selection, like [[listBox()]].
- * As a result, the corresponding submitted value is an array.
- * @param string $name the name attribute of each checkbox.
- * @param string|array $selection the selected value(s).
- * @param array $items the data item used to generate the checkboxes.
- * The array keys are the labels, while the array values are the corresponding checkbox values.
- * Note that the labels will NOT be HTML-encoded, while the values will.
- * @param array $options options (name => config) for the checkbox list. The following options are supported:
- *
- * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
- * By setting this option, a hidden input will be generated.
- * - separator: string, the HTML code that separates items.
- * - item: callable, a callback that can be used to customize the generation of the HTML code
- * corresponding to a single item in $items. The signature of this callback must be:
- *
- * ~~~
- * function ($index, $label, $name, $checked, $value)
- * ~~~
- *
- * where $index is the zero-based index of the checkbox in the whole list; $label
- * is the label for the checkbox; and $name, $value and $checked represent the name,
- * value and the checked status of the checkbox input, respectively.
- * @return string the generated checkbox list
- */
- public static function checkboxList($name, $selection = null, $items = array(), $options = array())
- {
- if (substr($name, -2) !== '[]') {
- $name .= '[]';
- }
-
- $formatter = isset($options['item']) ? $options['item'] : null;
- $lines = array();
- $index = 0;
- foreach ($items as $value => $label) {
- $checked = $selection !== null &&
- (!is_array($selection) && !strcmp($value, $selection)
- || is_array($selection) && in_array($value, $selection));
- if ($formatter !== null) {
- $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
- } else {
- $lines[] = static::label(static::checkbox($name, $checked, array('value' => $value)) . ' ' . $label);
- }
- $index++;
- }
-
- if (isset($options['unselect'])) {
- // add a hidden field so that if the list box has no option being selected, it still submits a value
- $name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;
- $hidden = static::hiddenInput($name2, $options['unselect']);
- } else {
- $hidden = '';
- }
- $separator = isset($options['separator']) ? $options['separator'] : "\n";
-
- return $hidden . implode($separator, $lines);
- }
-
- /**
- * Generates a list of radio buttons.
- * A radio button list is like a checkbox list, except that it only allows single selection.
- * @param string $name the name attribute of each radio button.
- * @param string|array $selection the selected value(s).
- * @param array $items the data item used to generate the radio buttons.
- * The array keys are the labels, while the array values are the corresponding radio button values.
- * Note that the labels will NOT be HTML-encoded, while the values will.
- * @param array $options options (name => config) for the radio button list. The following options are supported:
- *
- * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
- * By setting this option, a hidden input will be generated.
- * - separator: string, the HTML code that separates items.
- * - item: callable, a callback that can be used to customize the generation of the HTML code
- * corresponding to a single item in $items. The signature of this callback must be:
- *
- * ~~~
- * function ($index, $label, $name, $checked, $value)
- * ~~~
- *
- * where $index is the zero-based index of the radio button in the whole list; $label
- * is the label for the radio button; and $name, $value and $checked represent the name,
- * value and the checked status of the radio button input, respectively.
- * @return string the generated radio button list
- */
- public static function radioList($name, $selection = null, $items = array(), $options = array())
- {
- $formatter = isset($options['item']) ? $options['item'] : null;
- $lines = array();
- $index = 0;
- foreach ($items as $value => $label) {
- $checked = $selection !== null &&
- (!is_array($selection) && !strcmp($value, $selection)
- || is_array($selection) && in_array($value, $selection));
- if ($formatter !== null) {
- $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
- } else {
- $lines[] = static::label(static::radio($name, $checked, array('value' => $value)) . ' ' . $label);
- }
- $index++;
- }
-
- $separator = isset($options['separator']) ? $options['separator'] : "\n";
- if (isset($options['unselect'])) {
- // add a hidden field so that if the list box has no option being selected, it still submits a value
- $hidden = static::hiddenInput($name, $options['unselect']);
- } else {
- $hidden = '';
- }
-
- return $hidden . implode($separator, $lines);
- }
-
- /**
- * Generates a label tag for the given model attribute.
- * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]].
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * If a value is null, the corresponding attribute will not be rendered.
- * The following options are specially handled:
- *
- * - label: this specifies the label to be displayed. Note that this will NOT be [[encoded()]].
- * If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display
- * (after encoding).
- *
- * @return string the generated label tag
- */
- public static function activeLabel($model, $attribute, $options = array())
- {
- $attribute = static::getAttributeName($attribute);
- $label = isset($options['label']) ? $options['label'] : static::encode($model->getAttributeLabel($attribute));
- $for = array_key_exists('for', $options) ? $options['for'] : static::getInputId($model, $attribute);
- unset($options['label'], $options['for']);
- return static::label($label, $for, $options);
- }
-
- /**
- * Generates an input tag for the given model attribute.
- * This method will generate the "name" and "value" tag attributes automatically for the model attribute
- * unless they are explicitly specified in `$options`.
- * @param string $type the input type (e.g. 'text', 'password')
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the generated input tag
- */
- public static function activeInput($type, $model, $attribute, $options = array())
- {
- $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
- $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);
- if (!array_key_exists('id', $options)) {
- $options['id'] = static::getInputId($model, $attribute);
- }
- return static::input($type, $name, $value, $options);
- }
-
- /**
- * Generates a text input tag for the given model attribute.
- * This method will generate the "name" and "value" tag attributes automatically for the model attribute
- * unless they are explicitly specified in `$options`.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the generated input tag
- */
- public static function activeTextInput($model, $attribute, $options = array())
- {
- return static::activeInput('text', $model, $attribute, $options);
- }
-
- /**
- * Generates a hidden input tag for the given model attribute.
- * This method will generate the "name" and "value" tag attributes automatically for the model attribute
- * unless they are explicitly specified in `$options`.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the generated input tag
- */
- public static function activeHiddenInput($model, $attribute, $options = array())
- {
- return static::activeInput('hidden', $model, $attribute, $options);
- }
-
- /**
- * Generates a password input tag for the given model attribute.
- * This method will generate the "name" and "value" tag attributes automatically for the model attribute
- * unless they are explicitly specified in `$options`.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the generated input tag
- */
- public static function activePasswordInput($model, $attribute, $options = array())
- {
- return static::activeInput('password', $model, $attribute, $options);
- }
-
- /**
- * Generates a file input tag for the given model attribute.
- * This method will generate the "name" and "value" tag attributes automatically for the model attribute
- * unless they are explicitly specified in `$options`.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the generated input tag
- */
- public static function activeFileInput($model, $attribute, $options = array())
- {
- return static::activeInput('file', $model, $attribute, $options);
- }
-
- /**
- * Generates a textarea tag for the given model attribute.
- * The model attribute value will be used as the content in the textarea.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the generated textarea tag
- */
- public static function activeTextarea($model, $attribute, $options = array())
- {
- $name = static::getInputName($model, $attribute);
- $value = static::getAttributeValue($model, $attribute);
- if (!array_key_exists('id', $options)) {
- $options['id'] = static::getInputId($model, $attribute);
- }
- return static::textarea($name, $value, $options);
- }
-
- /**
- * Generates a radio button tag for the given model attribute.
- * This method will generate the "name" tag attribute automatically unless it is explicitly specified in `$options`.
- * This method will generate the "checked" tag attribute according to the model attribute value.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
- *
- * - uncheck: string, the value associated with the uncheck state of the radio button. If not set,
- * it will take the default value '0'. This method will render a hidden input so that if the radio button
- * is not checked and is submitted, the value of this attribute will still be submitted to the server
- * via the hidden input.
- *
- * The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
- *
- * @return string the generated radio button tag
- */
- public static function activeRadio($model, $attribute, $options = array())
- {
- $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
- $checked = static::getAttributeValue($model, $attribute);
- if (!array_key_exists('uncheck', $options)) {
- $options['uncheck'] = '0';
- }
- if (!array_key_exists('id', $options)) {
- $options['id'] = static::getInputId($model, $attribute);
- }
- return static::radio($name, $checked, $options);
- }
-
- /**
- * Generates a checkbox tag for the given model attribute.
- * This method will generate the "name" tag attribute automatically unless it is explicitly specified in `$options`.
- * This method will generate the "checked" tag attribute according to the model attribute value.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
- *
- * - uncheck: string, the value associated with the uncheck state of the radio button. If not set,
- * it will take the default value '0'. This method will render a hidden input so that if the radio button
- * is not checked and is submitted, the value of this attribute will still be submitted to the server
- * via the hidden input.
- *
- * The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
- *
- * @return string the generated checkbox tag
- */
- public static function activeCheckbox($model, $attribute, $options = array())
- {
- $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
- $checked = static::getAttributeValue($model, $attribute);
- if (!array_key_exists('uncheck', $options)) {
- $options['uncheck'] = '0';
- }
- if (!array_key_exists('id', $options)) {
- $options['id'] = static::getInputId($model, $attribute);
- }
- return static::checkbox($name, $checked, $options);
- }
-
- /**
- * Generates a drop-down list for the given model attribute.
- * The selection of the drop-down list is taken from the value of the model attribute.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $items the option data items. The array keys are option values, and the array values
- * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
- * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
- * If you have a list of data models, you may convert them into the format described above using
- * [[\yii\helpers\ArrayHelper::map()]].
- *
- * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
- * the labels will also be HTML-encoded.
- * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
- *
- * - prompt: string, a prompt text to be displayed as the first option;
- * - options: array, the attributes for the select option tags. The array keys must be valid option values,
- * and the array values are the extra attributes for the corresponding option tags. For example,
- *
- * ~~~
- * array(
- * 'value1' => array('disabled' => true),
- * 'value2' => array('label' => 'value 2'),
- * );
- * ~~~
- *
- * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
- * except that the array keys represent the optgroup labels specified in $items.
- *
- * The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
- *
- * @return string the generated drop-down list tag
- */
- public static function activeDropDownList($model, $attribute, $items, $options = array())
- {
- $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
- $checked = static::getAttributeValue($model, $attribute);
- if (!array_key_exists('id', $options)) {
- $options['id'] = static::getInputId($model, $attribute);
- }
- return static::dropDownList($name, $checked, $items, $options);
- }
-
- /**
- * Generates a list box.
- * The selection of the list box is taken from the value of the model attribute.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $items the option data items. The array keys are option values, and the array values
- * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
- * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
- * If you have a list of data models, you may convert them into the format described above using
- * [[\yii\helpers\ArrayHelper::map()]].
- *
- * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
- * the labels will also be HTML-encoded.
- * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
- *
- * - prompt: string, a prompt text to be displayed as the first option;
- * - options: array, the attributes for the select option tags. The array keys must be valid option values,
- * and the array values are the extra attributes for the corresponding option tags. For example,
- *
- * ~~~
- * array(
- * 'value1' => array('disabled' => true),
- * 'value2' => array('label' => 'value 2'),
- * );
- * ~~~
- *
- * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
- * except that the array keys represent the optgroup labels specified in $items.
- * - unselect: string, the value that will be submitted when no option is selected.
- * When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
- * mode, we can still obtain the posted unselect value.
- *
- * The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
- *
- * @return string the generated list box tag
- */
- public static function activeListBox($model, $attribute, $items, $options = array())
- {
- $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
- $checked = static::getAttributeValue($model, $attribute);
- if (!array_key_exists('unselect', $options)) {
- $options['unselect'] = '0';
- }
- if (!array_key_exists('id', $options)) {
- $options['id'] = static::getInputId($model, $attribute);
- }
- return static::listBox($name, $checked, $items, $options);
- }
-
- /**
- * Generates a list of checkboxes.
- * A checkbox list allows multiple selection, like [[listBox()]].
- * As a result, the corresponding submitted value is an array.
- * The selection of the checkbox list is taken from the value of the model attribute.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $items the data item used to generate the checkboxes.
- * The array keys are the labels, while the array values are the corresponding checkbox values.
- * Note that the labels will NOT be HTML-encoded, while the values will.
- * @param array $options options (name => config) for the checkbox list. The following options are specially handled:
- *
- * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
- * By setting this option, a hidden input will be generated.
- * - separator: string, the HTML code that separates items.
- * - item: callable, a callback that can be used to customize the generation of the HTML code
- * corresponding to a single item in $items. The signature of this callback must be:
- *
- * ~~~
- * function ($index, $label, $name, $checked, $value)
- * ~~~
- *
- * where $index is the zero-based index of the checkbox in the whole list; $label
- * is the label for the checkbox; and $name, $value and $checked represent the name,
- * value and the checked status of the checkbox input.
- * @return string the generated checkbox list
- */
- public static function activeCheckboxList($model, $attribute, $items, $options = array())
- {
- $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
- $checked = static::getAttributeValue($model, $attribute);
- if (!array_key_exists('unselect', $options)) {
- $options['unselect'] = '0';
- }
- return static::checkboxList($name, $checked, $items, $options);
- }
-
- /**
- * Generates a list of radio buttons.
- * A radio button list is like a checkbox list, except that it only allows single selection.
- * The selection of the radio buttons is taken from the value of the model attribute.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
- * about attribute expression.
- * @param array $items the data item used to generate the radio buttons.
- * The array keys are the labels, while the array values are the corresponding radio button values.
- * Note that the labels will NOT be HTML-encoded, while the values will.
- * @param array $options options (name => config) for the radio button list. The following options are specially handled:
- *
- * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
- * By setting this option, a hidden input will be generated.
- * - separator: string, the HTML code that separates items.
- * - item: callable, a callback that can be used to customize the generation of the HTML code
- * corresponding to a single item in $items. The signature of this callback must be:
- *
- * ~~~
- * function ($index, $label, $name, $checked, $value)
- * ~~~
- *
- * where $index is the zero-based index of the radio button in the whole list; $label
- * is the label for the radio button; and $name, $value and $checked represent the name,
- * value and the checked status of the radio button input.
- * @return string the generated radio button list
- */
- public static function activeRadioList($model, $attribute, $items, $options = array())
- {
- $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
- $checked = static::getAttributeValue($model, $attribute);
- if (!array_key_exists('unselect', $options)) {
- $options['unselect'] = '0';
- }
- return static::radioList($name, $checked, $items, $options);
- }
-
- /**
- * Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]].
- * @param string|array $selection the selected value(s). This can be either a string for single selection
- * or an array for multiple selections.
- * @param array $items the option data items. The array keys are option values, and the array values
- * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
- * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
- * If you have a list of data models, you may convert them into the format described above using
- * [[\yii\helpers\ArrayHelper::map()]].
- *
- * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
- * the labels will also be HTML-encoded.
- * @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call.
- * This method will take out these elements, if any: "prompt", "options" and "groups". See more details
- * in [[dropDownList()]] for the explanation of these elements.
- *
- * @return string the generated list options
- */
- public static function renderSelectOptions($selection, $items, &$tagOptions = array())
- {
- $lines = array();
- if (isset($tagOptions['prompt'])) {
- $prompt = str_replace(' ', ' ', static::encode($tagOptions['prompt']));
- $lines[] = static::tag('option', $prompt, array('value' => ''));
- }
-
- $options = isset($tagOptions['options']) ? $tagOptions['options'] : array();
- $groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : array();
- unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);
-
- foreach ($items as $key => $value) {
- if (is_array($value)) {
- $groupAttrs = isset($groups[$key]) ? $groups[$key] : array();
- $groupAttrs['label'] = $key;
- $attrs = array('options' => $options, 'groups' => $groups);
- $content = static::renderSelectOptions($selection, $value, $attrs);
- $lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs);
- } else {
- $attrs = isset($options[$key]) ? $options[$key] : array();
- $attrs['value'] = $key;
- $attrs['selected'] = $selection !== null &&
- (!is_array($selection) && !strcmp($key, $selection)
- || is_array($selection) && in_array($key, $selection));
- $lines[] = static::tag('option', str_replace(' ', ' ', static::encode($value)), $attrs);
- }
- }
-
- return implode("\n", $lines);
- }
-
- /**
- * Renders the HTML tag attributes.
- * Boolean attributes such as s 'checked', 'disabled', 'readonly', will be handled specially
- * according to [[booleanAttributes]] and [[showBooleanAttributeValues]].
- * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]].
- * Attributes whose value is null will be ignored and not put in the rendering result.
- * @return string the rendering result. If the attributes are not empty, they will be rendered
- * into a string with a leading white space (such that it can be directly appended to the tag name
- * in a tag. If there is no attribute, an empty string will be returned.
- */
- public static function renderTagAttributes($attributes)
- {
- if (count($attributes) > 1) {
- $sorted = array();
- foreach (static::$attributeOrder as $name) {
- if (isset($attributes[$name])) {
- $sorted[$name] = $attributes[$name];
- }
- }
- $attributes = array_merge($sorted, $attributes);
- }
-
- $html = '';
- foreach ($attributes as $name => $value) {
- if (isset(static::$booleanAttributes[strtolower($name)])) {
- if ($value || strcasecmp($name, $value) === 0) {
- $html .= static::$showBooleanAttributeValues ? " $name=\"$name\"" : " $name";
- }
- } elseif ($value !== null) {
- $html .= " $name=\"" . static::encode($value) . '"';
- }
- }
- return $html;
- }
-
- /**
- * Normalizes the input parameter to be a valid URL.
- *
- * If the input parameter
- *
- * - is an empty string: the currently requested URL will be returned;
- * - is a non-empty string: it will first be processed by [[Yii::getAlias()]]. If the result
- * is an absolute URL, it will be returned with any change further; Otherwise, the result
- * will be prefixed with [[\yii\web\Request::baseUrl]] and returned.
- * - is an array: the first array element is considered a route, while the rest of the name-value
- * pairs are treated as the parameters to be used for URL creation using [[\yii\web\Controller::createUrl()]].
- * For example: `array('post/index', 'page' => 2)`, `array('index')`.
- *
- * @param array|string $url the parameter to be used to generate a valid URL
- * @return string the normalized URL
- * @throws InvalidParamException if the parameter is invalid.
- */
- public static function url($url)
- {
- if (is_array($url)) {
- if (isset($url[0])) {
- $route = $url[0];
- $params = array_splice($url, 1);
- if (Yii::$app->controller !== null) {
- return Yii::$app->controller->createUrl($route, $params);
- } else {
- return Yii::$app->getUrlManager()->createUrl($route, $params);
- }
- } else {
- throw new InvalidParamException('The array specifying a URL must contain at least one element.');
- }
- } elseif ($url === '') {
- return Yii::$app->getRequest()->getUrl();
- } else {
- $url = Yii::getAlias($url);
- if ($url[0] === '/' || $url[0] === '#' || strpos($url, '://')) {
- return $url;
- } else {
- return Yii::$app->getRequest()->getBaseUrl() . '/' . $url;
- }
- }
- }
-
- /**
- * Returns the real attribute name from the given attribute expression.
- *
- * An attribute expression is an attribute name prefixed and/or suffixed with array indexes.
- * It is mainly used in tabular data input and/or input of array type. Below are some examples:
- *
- * - `[0]content` is used in tabular data input to represent the "content" attribute
- * for the first model in tabular input;
- * - `dates[0]` represents the first array element of the "dates" attribute;
- * - `[0]dates[0]` represents the first array element of the "dates" attribute
- * for the first model in tabular input.
- *
- * If `$attribute` has neither prefix nor suffix, it will be returned back without change.
- * @param string $attribute the attribute name or expression
- * @return string the attribute name without prefix and suffix.
- * @throws InvalidParamException if the attribute name contains non-word characters.
- */
- public static function getAttributeName($attribute)
- {
- if (preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) {
- return $matches[2];
- } else {
- throw new InvalidParamException('Attribute name must contain word characters only.');
- }
- }
-
- /**
- * Returns the value of the specified attribute name or expression.
- *
- * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`.
- * See [[getAttributeName()]] for more details about attribute expression.
- *
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression
- * @return mixed the corresponding attribute value
- * @throws InvalidParamException if the attribute name contains non-word characters.
- */
- public static function getAttributeValue($model, $attribute)
- {
- if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) {
- throw new InvalidParamException('Attribute name must contain word characters only.');
- }
- $attribute = $matches[2];
- $index = $matches[3];
- if ($index === '') {
- return $model->$attribute;
- } else {
- $value = $model->$attribute;
- foreach (explode('][', trim($index, '[]')) as $id) {
- if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) {
- $value = $value[$id];
- } else {
- return null;
- }
- }
- return $value;
- }
- }
-
- /**
- * Generates an appropriate input name for the specified attribute name or expression.
- *
- * This method generates a name that can be used as the input name to collect user input
- * for the specified attribute. The name is generated according to the [[Model::formName|form name]]
- * of the model and the given attribute name. For example, if the form name of the `Post` model
- * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`.
- *
- * See [[getAttributeName()]] for explanation of attribute expression.
- *
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression
- * @return string the generated input name
- * @throws InvalidParamException if the attribute name contains non-word characters.
- */
- public static function getInputName($model, $attribute)
- {
- $formName = $model->formName();
- if (!preg_match('/(^|.*\])(\w+)(\[.*|$)/', $attribute, $matches)) {
- throw new InvalidParamException('Attribute name must contain word characters only.');
- }
- $prefix = $matches[1];
- $attribute = $matches[2];
- $suffix = $matches[3];
- if ($formName === '' && $prefix === '') {
- return $attribute . $suffix;
- } elseif ($formName !== '') {
- return $formName . $prefix . "[$attribute]" . $suffix;
- } else {
- throw new InvalidParamException(get_class($model) . '::formName() cannot be empty for tabular inputs.');
- }
- }
-
- /**
- * Generates an appropriate input ID for the specified attribute name or expression.
- *
- * This method converts the result [[getInputName()]] into a valid input ID.
- * For example, [[getInputName()]] returns `Post[content]`, this method will return `Post-method`.
- * @param Model $model the model object
- * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression.
- * @return string the generated input ID
- * @throws InvalidParamException if the attribute name contains non-word characters.
- */
- public static function getInputId($model, $attribute)
- {
- $name = strtolower(static::getInputName($model, $attribute));
- return str_replace(array('[]', '][', '[', ']', ' '), array('', '-', '-', '', '-'), $name);
- }
-}
diff --git a/framework/yii/helpers/base/HtmlPurifier.php b/framework/yii/helpers/base/HtmlPurifier.php
deleted file mode 100644
index 799dabf..0000000
--- a/framework/yii/helpers/base/HtmlPurifier.php
+++ /dev/null
@@ -1,32 +0,0 @@
-
- * @since 2.0
- */
-class HtmlPurifier
-{
- /**
- * Passes markup through HTMLPurifier making it safe to output to end user
- *
- * @param string $content
- * @param array|null $config
- * @return string
- */
- public static function process($content, $config = null)
- {
- $purifier=\HTMLPurifier::instance($config);
- $purifier->config->set('Cache.SerializerPath', \Yii::$app->getRuntimePath());
- return $purifier->purify($content);
- }
-}
diff --git a/framework/yii/helpers/base/Inflector.php b/framework/yii/helpers/base/Inflector.php
deleted file mode 100644
index 113d350..0000000
--- a/framework/yii/helpers/base/Inflector.php
+++ /dev/null
@@ -1,519 +0,0 @@
-
- * @since 2.0
- */
-class Inflector
-{
- /**
- * @var array the rules for converting a word into its plural form.
- * The keys are the regular expressions and the values are the corresponding replacements.
- */
- public static $plurals = array(
- '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media)$/i' => '\1',
- '/^(sea[- ]bass)$/i' => '\1',
- '/(m)ove$/i' => '\1oves',
- '/(f)oot$/i' => '\1eet',
- '/(h)uman$/i' => '\1umans',
- '/(s)tatus$/i' => '\1tatuses',
- '/(s)taff$/i' => '\1taff',
- '/(t)ooth$/i' => '\1eeth',
- '/(quiz)$/i' => '\1zes',
- '/^(ox)$/i' => '\1\2en',
- '/([m|l])ouse$/i' => '\1ice',
- '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
- '/(x|ch|ss|sh)$/i' => '\1es',
- '/([^aeiouy]|qu)y$/i' => '\1ies',
- '/(hive)$/i' => '\1s',
- '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
- '/sis$/i' => 'ses',
- '/([ti])um$/i' => '\1a',
- '/(p)erson$/i' => '\1eople',
- '/(m)an$/i' => '\1en',
- '/(c)hild$/i' => '\1hildren',
- '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\1oes',
- '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
- '/us$/i' => 'uses',
- '/(alias)$/i' => '\1es',
- '/(ax|cris|test)is$/i' => '\1es',
- '/s$/' => 's',
- '/^$/' => '',
- '/$/' => 's',
- );
- /**
- * @var array the rules for converting a word into its singular form.
- * The keys are the regular expressions and the values are the corresponding replacements.
- */
- public static $singulars = array(
- '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media|ss)$/i' => '\1',
- '/^(sea[- ]bass)$/i' => '\1',
- '/(s)tatuses$/i' => '\1tatus',
- '/(f)eet$/i' => '\1oot',
- '/(t)eeth$/i' => '\1ooth',
- '/^(.*)(menu)s$/i' => '\1\2',
- '/(quiz)zes$/i' => '\\1',
- '/(matr)ices$/i' => '\1ix',
- '/(vert|ind)ices$/i' => '\1ex',
- '/^(ox)en/i' => '\1',
- '/(alias)(es)*$/i' => '\1',
- '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
- '/([ftw]ax)es/i' => '\1',
- '/(cris|ax|test)es$/i' => '\1is',
- '/(shoe|slave)s$/i' => '\1',
- '/(o)es$/i' => '\1',
- '/ouses$/' => 'ouse',
- '/([^a])uses$/' => '\1us',
- '/([m|l])ice$/i' => '\1ouse',
- '/(x|ch|ss|sh)es$/i' => '\1',
- '/(m)ovies$/i' => '\1\2ovie',
- '/(s)eries$/i' => '\1\2eries',
- '/([^aeiouy]|qu)ies$/i' => '\1y',
- '/([lr])ves$/i' => '\1f',
- '/(tive)s$/i' => '\1',
- '/(hive)s$/i' => '\1',
- '/(drive)s$/i' => '\1',
- '/([^fo])ves$/i' => '\1fe',
- '/(^analy)ses$/i' => '\1sis',
- '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
- '/([ti])a$/i' => '\1um',
- '/(p)eople$/i' => '\1\2erson',
- '/(m)en$/i' => '\1an',
- '/(c)hildren$/i' => '\1\2hild',
- '/(n)ews$/i' => '\1\2ews',
- '/eaus$/' => 'eau',
- '/^(.*us)$/' => '\\1',
- '/s$/i' => '',
- );
- /**
- * @var array the special rules for converting a word between its plural form and singular form.
- * The keys are the special words in singular form, and the values are the corresponding plural form.
- */
- public static $specials = array(
- 'atlas' => 'atlases',
- 'beef' => 'beefs',
- 'brother' => 'brothers',
- 'cafe' => 'cafes',
- 'child' => 'children',
- 'cookie' => 'cookies',
- 'corpus' => 'corpuses',
- 'cow' => 'cows',
- 'curve' => 'curves',
- 'foe' => 'foes',
- 'ganglion' => 'ganglions',
- 'genie' => 'genies',
- 'genus' => 'genera',
- 'graffito' => 'graffiti',
- 'hoof' => 'hoofs',
- 'loaf' => 'loaves',
- 'man' => 'men',
- 'money' => 'monies',
- 'mongoose' => 'mongooses',
- 'move' => 'moves',
- 'mythos' => 'mythoi',
- 'niche' => 'niches',
- 'numen' => 'numina',
- 'occiput' => 'occiputs',
- 'octopus' => 'octopuses',
- 'opus' => 'opuses',
- 'ox' => 'oxen',
- 'penis' => 'penises',
- 'sex' => 'sexes',
- 'soliloquy' => 'soliloquies',
- 'testis' => 'testes',
- 'trilby' => 'trilbys',
- 'turf' => 'turfs',
- 'wave' => 'waves',
- 'Amoyese' => 'Amoyese',
- 'bison' => 'bison',
- 'Borghese' => 'Borghese',
- 'bream' => 'bream',
- 'breeches' => 'breeches',
- 'britches' => 'britches',
- 'buffalo' => 'buffalo',
- 'cantus' => 'cantus',
- 'carp' => 'carp',
- 'chassis' => 'chassis',
- 'clippers' => 'clippers',
- 'cod' => 'cod',
- 'coitus' => 'coitus',
- 'Congoese' => 'Congoese',
- 'contretemps' => 'contretemps',
- 'corps' => 'corps',
- 'debris' => 'debris',
- 'diabetes' => 'diabetes',
- 'djinn' => 'djinn',
- 'eland' => 'eland',
- 'elk' => 'elk',
- 'equipment' => 'equipment',
- 'Faroese' => 'Faroese',
- 'flounder' => 'flounder',
- 'Foochowese' => 'Foochowese',
- 'gallows' => 'gallows',
- 'Genevese' => 'Genevese',
- 'Genoese' => 'Genoese',
- 'Gilbertese' => 'Gilbertese',
- 'graffiti' => 'graffiti',
- 'headquarters' => 'headquarters',
- 'herpes' => 'herpes',
- 'hijinks' => 'hijinks',
- 'Hottentotese' => 'Hottentotese',
- 'information' => 'information',
- 'innings' => 'innings',
- 'jackanapes' => 'jackanapes',
- 'Kiplingese' => 'Kiplingese',
- 'Kongoese' => 'Kongoese',
- 'Lucchese' => 'Lucchese',
- 'mackerel' => 'mackerel',
- 'Maltese' => 'Maltese',
- 'mews' => 'mews',
- 'moose' => 'moose',
- 'mumps' => 'mumps',
- 'Nankingese' => 'Nankingese',
- 'news' => 'news',
- 'nexus' => 'nexus',
- 'Niasese' => 'Niasese',
- 'Pekingese' => 'Pekingese',
- 'Piedmontese' => 'Piedmontese',
- 'pincers' => 'pincers',
- 'Pistoiese' => 'Pistoiese',
- 'pliers' => 'pliers',
- 'Portuguese' => 'Portuguese',
- 'proceedings' => 'proceedings',
- 'rabies' => 'rabies',
- 'rice' => 'rice',
- 'rhinoceros' => 'rhinoceros',
- 'salmon' => 'salmon',
- 'Sarawakese' => 'Sarawakese',
- 'scissors' => 'scissors',
- 'series' => 'series',
- 'Shavese' => 'Shavese',
- 'shears' => 'shears',
- 'siemens' => 'siemens',
- 'species' => 'species',
- 'swine' => 'swine',
- 'testes' => 'testes',
- 'trousers' => 'trousers',
- 'trout' => 'trout',
- 'tuna' => 'tuna',
- 'Vermontese' => 'Vermontese',
- 'Wenchowese' => 'Wenchowese',
- 'whiting' => 'whiting',
- 'wildebeest' => 'wildebeest',
- 'Yengeese' => 'Yengeese',
- );
- /**
- * @var array map of special chars and its translation. This is used by [[slug()]] and [[ascii()]].
- */
- protected static $transliteration = array(
- '/Æ|Ǽ/' => 'AE',
- '/ä|æ|ǽ/' => 'ae',
- '/ö|œ/' => 'oe',
- '/ü/' => 'ue',
- '/Ä/' => 'Ae',
- '/Ü/' => 'Ue',
- '/Ö/' => 'Oe',
- '/Ψ/' => 'PS',
- '/ψ/' => 'ps',
- '/À|Á|Â|Ã|Å|Ǻ|Ā|Ă|Ą|Ǎ|Ά/' => 'A',
- '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª|ά/' => 'a',
- '/Б/' => 'B',
- '/β|б/' => 'b',
- '/Ç|Ć|Ĉ|Ċ|Č|Ц/' => 'C',
- '/ç|ć|ĉ|ċ|č|ц/' => 'c',
- '/Ч/' => 'Ch',
- '/ч/' => 'ch',
- '/©/' => '(c)',
- '/Ð|Ď|Đ|Δ|Д/' => 'D',
- '/ð|ď|đ|δ|д/' => 'd',
- '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Έ|Э/' => 'E',
- '/è|é|ê|ë|ē|ĕ|ė|ę|ě|ε|έ|э/' => 'e',
- '/Φ|Ф/' => 'F',
- '/φ|ƒ|ф/' => 'f',
- '/Ĝ|Ğ|Ġ|Ģ|Γ|Ґ/' => 'G',
- '/ĝ|ğ|ġ|ģ|γ|г|ґ/' => 'g',
- '/Ĥ|Ħ|Ή/' => 'H',
- '/ĥ|ħ|η|ή|н|х/' => 'h',
- '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|Ί|И/' => 'I',
- '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|ι|ί|ϊ|ΐ|и/' => 'i',
- '/Ĵ|Й/' => 'J',
- '/ĵ|й/' => 'j',
- '/Ķ/' => 'K',
- '/ķ|κ/' => 'k',
- '/Ĺ|Ļ|Ľ|Ŀ|Ł|Λ|Л/' => 'L',
- '/ĺ|ļ|ľ|ŀ|ł|λ|л/' => 'l',
- '/μ|м/' => 'm',
- '/Ñ|Ń|Ņ|Ň/' => 'N',
- '/ñ|ń|ņ|ň|ʼn|ν/' => 'n',
- '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|Ό/' => 'O',
- '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|ο/' => 'o',
- '/Π/' => 'P',
- '/π|п/' => 'p',
- '/Ŕ|Ŗ|Ř/' => 'R',
- '/ŕ|ŗ|ř|ρ|р/' => 'r',
- '/Ś|Ŝ|Ş|Ș|Š|Σ/' => 'S',
- '/ś|ŝ|ş|ș|š|ſ|σ|ς|с/' => 's',
- '/ß/' => 'ss',
- '/ẞ/' => 'SS',
- '/Ţ|Ț|Ť|Ŧ|τ/' => 'T',
- '/ţ|ț|ť|ŧ|т/' => 't',
- '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|У/' => 'U',
- '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u',
- '/в/' => 'v',
- '/χ/' => 'x',
- '/Ý|Ÿ|Ŷ|Ύ|Ϋ/' => 'Y',
- '/ý|ÿ|ŷ|υ|ύ|ΰ|ы/' => 'y',
- '/Ŵ|Ω|Ώ/' => 'W',
- '/ŵ|ω|ώ/' => 'w',
- '/Ź|Ż|Ž|З/' => 'Z',
- '/ź|ż|ž|ζ|з/' => 'z',
- '/IJ/' => 'IJ',
- '/ij/' => 'ij',
- '/Œ/' => 'OE',
- '/Ш|Щ/' => 'Sh',
- '/ш|щ/' => 'sh',
- '/Я/' => 'Ya',
- '/я/' => 'ya',
- '/Є/' => 'Ye',
- '/є/' => 'ye',
- '/Ї/' => 'Yi',
- '/ї/' => 'yi',
- '/Ё/' => 'Yo',
- '/ё/' => 'yo',
- '/Ю/' => 'Yu',
- '/ю/' => 'yu',
- '/Ж/' => 'Zh',
- '/ж/' => 'zh',
- '/ξ|Ξ/' => '3',
- '/θ/' => '8',
- '/ъ|ь|Ъ|Ы|Ь/' => '',
- );
-
- /**
- * Converts a word to its plural form.
- * Note that this is for English only!
- * For example, 'apple' will become 'apples', and 'child' will become 'children'.
- * @param string $word the word to be pluralized
- * @return string the pluralized word
- */
- public static function pluralize($word)
- {
- if (isset(self::$specials[$word])) {
- return self::$specials[$word];
- }
- foreach (static::$plurals as $rule => $replacement) {
- if (preg_match($rule, $word)) {
- return preg_replace($rule, $replacement, $word);
- }
- }
- return $word;
- }
-
- /**
- * Returns the singular of the $word
- * @param string $word the english word to singularize
- * @return string Singular noun.
- */
- public static function singularize($word)
- {
- $result = array_search($word, self::$specials, true);
- if ($result !== false) {
- return $result;
- }
- foreach (static::$singulars as $rule => $replacement) {
- if (preg_match($rule, $word)) {
- return preg_replace($rule, $replacement, $word);
- }
- }
- return $word;
- }
-
- /**
- * Converts an underscored or CamelCase word into a English
- * sentence.
- * @param string $words
- * @param bool $ucAll whether to set all words to uppercase
- * @return string
- */
- public static function titleize($words, $ucAll = false)
- {
- $words = static::humanize(static::underscore($words), $ucAll);
- return $ucAll ? ucwords($words) : ucfirst($words);
- }
-
- /**
- * Returns given word as CamelCased
- * Converts a word like "send_email" to "SendEmail". It
- * will remove non alphanumeric character from the word, so
- * "who's online" will be converted to "WhoSOnline"
- * @see variablize
- * @param string $word the word to CamelCase
- * @return string
- */
- public static function camelize($word)
- {
- return str_replace(' ', '', ucwords(preg_replace('/[^A-Z^a-z^0-9]+/', ' ', $word)));
- }
-
- /**
- * Converts a CamelCase name into space-separated words.
- * For example, 'PostTag' will be converted to 'Post Tag'.
- * @param string $name the string to be converted
- * @param boolean $ucwords whether to capitalize the first letter in each word
- * @return string the resulting words
- */
- public static function camel2words($name, $ucwords = true)
- {
- $label = trim(strtolower(str_replace(array(
- '-',
- '_',
- '.'
- ), ' ', preg_replace('/(? ' ',
- '/\\s+/' => $replacement,
- '/(?<=[a-z])([A-Z])/' => $replacement . '\\1',
- str_replace(':rep', preg_quote($replacement, '/'), '/^[:rep]+|[:rep]+$/') => ''
- );
- return preg_replace(array_keys($map), array_values($map), static::ascii($string));
- }
-
- /**
- * Converts a table name to its class name. For example, converts "people" to "Person"
- * @param string $table_name
- * @return string
- */
- public static function classify($table_name)
- {
- return static::camelize(static::singularize($table_name));
- }
-
- /**
- * Converts number to its ordinal English form. For example, converts 13 to 13th, 2 to 2nd ...
- * @param int $number the number to get its ordinal value
- * @return string
- */
- public static function ordinalize($number)
- {
- if (in_array(($number % 100), range(11, 13))) {
- return $number . 'th';
- }
- switch ($number % 10) {
- case 1: return $number . 'st';
- case 2: return $number . 'nd';
- case 3: return $number . 'rd';
- default: return $number . 'th';
- }
- }
-
- /**+
- * Converts all special characters to the closest ascii character equivalent.
- * @param string $string the string to be converted.
- * @return string the translated
- */
- public static function ascii($string)
- {
- $map = static::$transliteration;
- return preg_replace(array_keys($map), array_values($map), $string);
- }
-}
diff --git a/framework/yii/helpers/base/Json.php b/framework/yii/helpers/base/Json.php
deleted file mode 100644
index 41d5bf0..0000000
--- a/framework/yii/helpers/base/Json.php
+++ /dev/null
@@ -1,111 +0,0 @@
-
- * @since 2.0
- */
-class Json
-{
- /**
- * Encodes the given value into a JSON string.
- * The method enhances `json_encode()` by supporting JavaScript expressions.
- * In particular, the method will not encode a JavaScript expression that is
- * represented in terms of a [[JsExpression]] object.
- * @param mixed $value the data to be encoded
- * @param integer $options the encoding options. For more details please refer to
- * [[http://www.php.net/manual/en/function.json-encode.php]]
- * @return string the encoding result
- */
- public static function encode($value, $options = 0)
- {
- $expressions = array();
- $value = static::processData($value, $expressions);
- $json = json_encode($value, $options);
- return empty($expressions) ? $json : strtr($json, $expressions);
- }
-
- /**
- * Decodes the given JSON string into a PHP data structure.
- * @param string $json the JSON string to be decoded
- * @param boolean $asArray whether to return objects in terms of associative arrays.
- * @return mixed the PHP data
- * @throws InvalidParamException if there is any decoding error
- */
- public static function decode($json, $asArray = true)
- {
- if (is_array($json)) {
- throw new InvalidParamException('Invalid JSON data.');
- }
- $decode = json_decode((string)$json, $asArray);
- switch (json_last_error()) {
- case JSON_ERROR_NONE:
- break;
- case JSON_ERROR_DEPTH:
- throw new InvalidParamException('The maximum stack depth has been exceeded.');
- case JSON_ERROR_CTRL_CHAR:
- throw new InvalidParamException('Control character error, possibly incorrectly encoded.');
- case JSON_ERROR_SYNTAX:
- throw new InvalidParamException('Syntax error.');
- case JSON_ERROR_STATE_MISMATCH:
- throw new InvalidParamException('Invalid or malformed JSON.');
- case JSON_ERROR_UTF8:
- throw new InvalidParamException('Malformed UTF-8 characters, possibly incorrectly encoded.');
- default:
- throw new InvalidParamException('Unknown JSON decoding error.');
- }
-
- return $decode;
- }
-
- /**
- * Pre-processes the data before sending it to `json_encode()`.
- * @param mixed $data the data to be processed
- * @param array $expressions collection of JavaScript expressions
- * @return mixed the processed data
- */
- protected static function processData($data, &$expressions)
- {
- if (is_array($data)) {
- foreach ($data as $key => $value) {
- if (is_array($value) || is_object($value)) {
- $data[$key] = static::processData($value, $expressions);
- }
- }
- return $data;
- } elseif (is_object($data)) {
- if ($data instanceof JsExpression) {
- $token = '!{[' . count($expressions) . ']}!';
- $expressions['"' . $token . '"'] = $data->expression;
- return $token;
- } elseif ($data instanceof Jsonable) {
- return $data->toJson();
- } else {
- $result = array();
- foreach ($data as $key => $value) {
- if (is_array($value) || is_object($value)) {
- $result[$key] = static::processData($value, $expressions);
- } else {
- $result[$key] = $value;
- }
- }
- return $result;
- }
- } else {
- return $data;
- }
- }
-}
diff --git a/framework/yii/helpers/base/Markdown.php b/framework/yii/helpers/base/Markdown.php
deleted file mode 100644
index 3e69015..0000000
--- a/framework/yii/helpers/base/Markdown.php
+++ /dev/null
@@ -1,57 +0,0 @@
- 'footnote_',
- * ));
- * ```
- *
- * For more details please refer to [PHP Markdown library documentation](http://michelf.ca/projects/php-markdown/).
- * @author Alexander Makarov
- * @since 2.0
- */
-class Markdown
-{
- /**
- * @var MarkdownExtra
- */
- protected static $markdown;
-
- /**
- * Converts markdown into HTML
- *
- * @param string $content
- * @param array $config
- * @return string
- */
- public static function process($content, $config = array())
- {
- if (static::$markdown === null) {
- static::$markdown = new MarkdownExtra();
- }
- foreach ($config as $name => $value) {
- static::$markdown->{$name} = $value;
- }
- return static::$markdown->transform($content);
- }
-}
diff --git a/framework/yii/helpers/base/SecurityHelper.php b/framework/yii/helpers/base/SecurityHelper.php
deleted file mode 100644
index f646a24..0000000
--- a/framework/yii/helpers/base/SecurityHelper.php
+++ /dev/null
@@ -1,290 +0,0 @@
-
- * @author Tom Worster
- * @since 2.0
- */
-class SecurityHelper
-{
- /**
- * Encrypts data.
- * @param string $data data to be encrypted.
- * @param string $key the encryption secret key
- * @return string the encrypted data
- * @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized
- * @see decrypt()
- */
- public static function encrypt($data, $key)
- {
- $module = static::openCryptModule();
- // 192-bit (24 bytes) key size
- $key = StringHelper::substr($key, 0, 24);
- srand();
- $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
- mcrypt_generic_init($module, $key, $iv);
- $encrypted = $iv . mcrypt_generic($module, $data);
- mcrypt_generic_deinit($module);
- mcrypt_module_close($module);
- return $encrypted;
- }
-
- /**
- * Decrypts data
- * @param string $data data to be decrypted.
- * @param string $key the decryption secret key
- * @return string the decrypted data
- * @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized
- * @see encrypt()
- */
- public static function decrypt($data, $key)
- {
- $module = static::openCryptModule();
- // 192-bit (24 bytes) key size
- $key = StringHelper::substr($key, 0, 24);
- $ivSize = mcrypt_enc_get_iv_size($module);
- $iv = StringHelper::substr($data, 0, $ivSize);
- mcrypt_generic_init($module, $key, $iv);
- $decrypted = mdecrypt_generic($module, StringHelper::substr($data, $ivSize, StringHelper::strlen($data)));
- mcrypt_generic_deinit($module);
- mcrypt_module_close($module);
- return rtrim($decrypted, "\0");
- }
-
- /**
- * Prefixes data with a keyed hash value so that it can later be detected if it is tampered.
- * @param string $data the data to be protected
- * @param string $key the secret key to be used for generating hash
- * @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()"
- * function to see the supported hashing algorithms on your system.
- * @return string the data prefixed with the keyed hash
- * @see validateData()
- * @see getSecretKey()
- */
- public static function hashData($data, $key, $algorithm = 'sha256')
- {
- return hash_hmac($algorithm, $data, $key) . $data;
- }
-
- /**
- * Validates if the given data is tampered.
- * @param string $data the data to be validated. The data must be previously
- * generated by [[hashData()]].
- * @param string $key the secret key that was previously used to generate the hash for the data in [[hashData()]].
- * @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()"
- * function to see the supported hashing algorithms on your system. This must be the same
- * as the value passed to [[hashData()]] when generating the hash for the data.
- * @return string the real data with the hash stripped off. False if the data is tampered.
- * @see hashData()
- */
- public static function validateData($data, $key, $algorithm = 'sha256')
- {
- $hashSize = StringHelper::strlen(hash_hmac($algorithm, 'test', $key));
- $n = StringHelper::strlen($data);
- if ($n >= $hashSize) {
- $hash = StringHelper::substr($data, 0, $hashSize);
- $data2 = StringHelper::substr($data, $hashSize, $n - $hashSize);
- return $hash === hash_hmac($algorithm, $data2, $key) ? $data2 : false;
- } else {
- return false;
- }
- }
-
- /**
- * Returns a secret key associated with the specified name.
- * If the secret key does not exist, a random key will be generated
- * and saved in the file "keys.php" under the application's runtime directory
- * so that the same secret key can be returned in future requests.
- * @param string $name the name that is associated with the secret key
- * @param integer $length the length of the key that should be generated if not exists
- * @return string the secret key associated with the specified name
- */
- public static function getSecretKey($name, $length = 32)
- {
- static $keys;
- $keyFile = Yii::$app->getRuntimePath() . '/keys.php';
- if ($keys === null) {
- $keys = is_file($keyFile) ? require($keyFile) : array();
- }
- if (!isset($keys[$name])) {
- $keys[$name] = static::generateRandomKey($length);
- file_put_contents($keyFile, " 30) {
- throw new InvalidParamException('Hash is invalid.');
- }
-
- $test = crypt($password, $hash);
- $n = strlen($test);
- if (strlen($test) < 32 || $n !== strlen($hash)) {
- return false;
- }
-
- // Use a for-loop to compare two strings to prevent timing attacks. See:
- // http://codereview.stackexchange.com/questions/13512
- $check = 0;
- for ($i = 0; $i < $n; ++$i) {
- $check |= (ord($test[$i]) ^ ord($hash[$i]));
- }
-
- return $check === 0;
- }
-
- /**
- * Generates a salt that can be used to generate a password hash.
- *
- * The PHP [crypt()](http://php.net/manual/en/function.crypt.php) built-in function
- * requires, for the Blowfish hash algorithm, a salt string in a specific format:
- * "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters
- * from the alphabet "./0-9A-Za-z".
- *
- * @param integer $cost the cost parameter
- * @return string the random salt value.
- * @throws InvalidParamException if the cost parameter is not between 4 and 30
- */
- protected static function generateSalt($cost = 13)
- {
- $cost = (int)$cost;
- if ($cost < 4 || $cost > 30) {
- throw new InvalidParamException('Cost must be between 4 and 31.');
- }
-
- // Get 20 * 8bits of pseudo-random entropy from mt_rand().
- $rand = '';
- for ($i = 0; $i < 20; ++$i) {
- $rand .= chr(mt_rand(0, 255));
- }
-
- // Add the microtime for a little more entropy.
- $rand .= microtime();
- // Mix the bits cryptographically into a 20-byte binary string.
- $rand = sha1($rand, true);
- // Form the prefix that specifies Blowfish algorithm and cost parameter.
- $salt = sprintf("$2y$%02d$", $cost);
- // Append the random salt data in the required base64 format.
- $salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22));
- return $salt;
- }
-}
diff --git a/framework/yii/helpers/base/StringHelper.php b/framework/yii/helpers/base/StringHelper.php
deleted file mode 100644
index 5134bf6..0000000
--- a/framework/yii/helpers/base/StringHelper.php
+++ /dev/null
@@ -1,65 +0,0 @@
-
- * @author Alex Makarov
- * @since 2.0
- */
-class StringHelper
-{
- /**
- * Returns the number of bytes in the given string.
- * This method ensures the string is treated as a byte array by using `mb_strlen()`.
- * @param string $string the string being measured for length
- * @return integer the number of bytes in the given string.
- */
- public static function strlen($string)
- {
- return mb_strlen($string, '8bit');
- }
-
- /**
- * Returns the portion of string specified by the start and length parameters.
- * This method ensures the string is treated as a byte array by using `mb_substr()`.
- * @param string $string the input string. Must be one character or longer.
- * @param integer $start the starting position
- * @param integer $length the desired portion length
- * @return string the extracted part of string, or FALSE on failure or an empty string.
- * @see http://www.php.net/manual/en/function.substr.php
- */
- public static function substr($string, $start, $length)
- {
- return mb_substr($string, $start, $length, '8bit');
- }
-
- /**
- * Returns the trailing name component of a path.
- * This method does the same as the php function basename() except that it will
- * always use \ and / as directory separators, independent of the operating system.
- * Note: this method is not aware of the actual filesystem, or path components such as "..".
- * @param string $path A path string.
- * @param string $suffix If the name component ends in suffix this will also be cut off.
- * @return string the trailing name component of the given path.
- * @see http://www.php.net/manual/en/function.basename.php
- */
- public static function basename($path, $suffix = '')
- {
- if (($len = mb_strlen($suffix)) > 0 && mb_substr($path, -$len) == $suffix) {
- $path = mb_substr($path, 0, -$len);
- }
- $path = rtrim(str_replace('\\', '/', $path), '/\\');
- if (($pos = mb_strrpos($path, '/')) !== false) {
- return mb_substr($path, $pos + 1);
- }
- return $path;
- }
-}
diff --git a/framework/yii/helpers/base/VarDumper.php b/framework/yii/helpers/base/VarDumper.php
deleted file mode 100644
index 730aafe..0000000
--- a/framework/yii/helpers/base/VarDumper.php
+++ /dev/null
@@ -1,134 +0,0 @@
-
- * @link http://www.yiiframework.com/
- * @copyright Copyright © 2008-2011 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\helpers\base;
-
-/**
- * VarDumper is intended to replace the buggy PHP function var_dump and print_r.
- * It can correctly identify the recursively referenced objects in a complex
- * object structure. It also has a recursive depth control to avoid indefinite
- * recursive display of some peculiar variables.
- *
- * VarDumper can be used as follows,
- *
- * ~~~
- * VarDumper::dump($var);
- * ~~~
- *
- * @author Qiang Xue
- * @since 2.0
- */
-class VarDumper
-{
- private static $_objects;
- private static $_output;
- private static $_depth;
-
- /**
- * Displays a variable.
- * This method achieves the similar functionality as var_dump and print_r
- * but is more robust when handling complex objects such as Yii controllers.
- * @param mixed $var variable to be dumped
- * @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10.
- * @param boolean $highlight whether the result should be syntax-highlighted
- */
- public static function dump($var, $depth = 10, $highlight = false)
- {
- echo static::dumpAsString($var, $depth, $highlight);
- }
-
- /**
- * Dumps a variable in terms of a string.
- * This method achieves the similar functionality as var_dump and print_r
- * but is more robust when handling complex objects such as Yii controllers.
- * @param mixed $var variable to be dumped
- * @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10.
- * @param boolean $highlight whether the result should be syntax-highlighted
- * @return string the string representation of the variable
- */
- public static function dumpAsString($var, $depth = 10, $highlight = false)
- {
- self::$_output = '';
- self::$_objects = array();
- self::$_depth = $depth;
- self::dumpInternal($var, 0);
- if ($highlight) {
- $result = highlight_string("/', '', $result, 1);
- }
- return self::$_output;
- }
-
- /**
- * @param mixed $var variable to be dumped
- * @param integer $level depth level
- */
- private static function dumpInternal($var, $level)
- {
- switch (gettype($var)) {
- case 'boolean':
- self::$_output .= $var ? 'true' : 'false';
- break;
- case 'integer':
- self::$_output .= "$var";
- break;
- case 'double':
- self::$_output .= "$var";
- break;
- case 'string':
- self::$_output .= "'" . addslashes($var) . "'";
- break;
- case 'resource':
- self::$_output .= '{resource}';
- break;
- case 'NULL':
- self::$_output .= "null";
- break;
- case 'unknown type':
- self::$_output .= '{unknown}';
- break;
- case 'array':
- if (self::$_depth <= $level) {
- self::$_output .= 'array(...)';
- } elseif (empty($var)) {
- self::$_output .= 'array()';
- } else {
- $keys = array_keys($var);
- $spaces = str_repeat(' ', $level * 4);
- self::$_output .= "array\n" . $spaces . '(';
- foreach ($keys as $key) {
- self::$_output .= "\n" . $spaces . ' ';
- self::dumpInternal($key, 0);
- self::$_output .= ' => ';
- self::dumpInternal($var[$key], $level + 1);
- }
- self::$_output .= "\n" . $spaces . ')';
- }
- break;
- case 'object':
- if (($id = array_search($var, self::$_objects, true)) !== false) {
- self::$_output .= get_class($var) . '#' . ($id + 1) . '(...)';
- } elseif (self::$_depth <= $level) {
- self::$_output .= get_class($var) . '(...)';
- } else {
- $id = array_push(self::$_objects, $var);
- $className = get_class($var);
- $members = (array)$var;
- $spaces = str_repeat(' ', $level * 4);
- self::$_output .= "$className#$id\n" . $spaces . '(';
- foreach ($members as $key => $value) {
- $keyDisplay = strtr(trim($key), array("\0" => ':'));
- self::$_output .= "\n" . $spaces . " [$keyDisplay] => ";
- self::dumpInternal($value, $level + 1);
- }
- self::$_output .= "\n" . $spaces . ')';
- }
- break;
- }
- }
-}
diff --git a/framework/yii/helpers/base/mimeTypes.php b/framework/yii/helpers/base/mimeTypes.php
deleted file mode 100644
index ffdba4b..0000000
--- a/framework/yii/helpers/base/mimeTypes.php
+++ /dev/null
@@ -1,187 +0,0 @@
- 'application/postscript',
- 'aif' => 'audio/x-aiff',
- 'aifc' => 'audio/x-aiff',
- 'aiff' => 'audio/x-aiff',
- 'anx' => 'application/annodex',
- 'asc' => 'text/plain',
- 'au' => 'audio/basic',
- 'avi' => 'video/x-msvideo',
- 'axa' => 'audio/annodex',
- 'axv' => 'video/annodex',
- 'bcpio' => 'application/x-bcpio',
- 'bin' => 'application/octet-stream',
- 'bmp' => 'image/bmp',
- 'c' => 'text/plain',
- 'cc' => 'text/plain',
- 'ccad' => 'application/clariscad',
- 'cdf' => 'application/x-netcdf',
- 'class' => 'application/octet-stream',
- 'cpio' => 'application/x-cpio',
- 'cpt' => 'application/mac-compactpro',
- 'csh' => 'application/x-csh',
- 'css' => 'text/css',
- 'dcr' => 'application/x-director',
- 'dir' => 'application/x-director',
- 'dms' => 'application/octet-stream',
- 'doc' => 'application/msword',
- 'drw' => 'application/drafting',
- 'dvi' => 'application/x-dvi',
- 'dwg' => 'application/acad',
- 'dxf' => 'application/dxf',
- 'dxr' => 'application/x-director',
- 'eps' => 'application/postscript',
- 'etx' => 'text/x-setext',
- 'exe' => 'application/octet-stream',
- 'ez' => 'application/andrew-inset',
- 'f' => 'text/plain',
- 'f90' => 'text/plain',
- 'flac' => 'audio/flac',
- 'fli' => 'video/x-fli',
- 'flv' => 'video/x-flv',
- 'gif' => 'image/gif',
- 'gtar' => 'application/x-gtar',
- 'gz' => 'application/x-gzip',
- 'h' => 'text/plain',
- 'hdf' => 'application/x-hdf',
- 'hh' => 'text/plain',
- 'hqx' => 'application/mac-binhex40',
- 'htm' => 'text/html',
- 'html' => 'text/html',
- 'ice' => 'x-conference/x-cooltalk',
- 'ief' => 'image/ief',
- 'iges' => 'model/iges',
- 'igs' => 'model/iges',
- 'ips' => 'application/x-ipscript',
- 'ipx' => 'application/x-ipix',
- 'jpe' => 'image/jpeg',
- 'jpeg' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'js' => 'application/x-javascript',
- 'kar' => 'audio/midi',
- 'latex' => 'application/x-latex',
- 'lha' => 'application/octet-stream',
- 'lsp' => 'application/x-lisp',
- 'lzh' => 'application/octet-stream',
- 'm' => 'text/plain',
- 'man' => 'application/x-troff-man',
- 'me' => 'application/x-troff-me',
- 'mesh' => 'model/mesh',
- 'mid' => 'audio/midi',
- 'midi' => 'audio/midi',
- 'mif' => 'application/vnd.mif',
- 'mime' => 'www/mime',
- 'mov' => 'video/quicktime',
- 'movie' => 'video/x-sgi-movie',
- 'mp2' => 'audio/mpeg',
- 'mp3' => 'audio/mpeg',
- 'mpe' => 'video/mpeg',
- 'mpeg' => 'video/mpeg',
- 'mpg' => 'video/mpeg',
- 'mpga' => 'audio/mpeg',
- 'ms' => 'application/x-troff-ms',
- 'msh' => 'model/mesh',
- 'nc' => 'application/x-netcdf',
- 'oga' => 'audio/ogg',
- 'ogg' => 'audio/ogg',
- 'ogv' => 'video/ogg',
- 'ogx' => 'application/ogg',
- 'oda' => 'application/oda',
- 'pbm' => 'image/x-portable-bitmap',
- 'pdb' => 'chemical/x-pdb',
- 'pdf' => 'application/pdf',
- 'pgm' => 'image/x-portable-graymap',
- 'pgn' => 'application/x-chess-pgn',
- 'png' => 'image/png',
- 'pnm' => 'image/x-portable-anymap',
- 'pot' => 'application/mspowerpoint',
- 'ppm' => 'image/x-portable-pixmap',
- 'pps' => 'application/mspowerpoint',
- 'ppt' => 'application/mspowerpoint',
- 'ppz' => 'application/mspowerpoint',
- 'pre' => 'application/x-freelance',
- 'prt' => 'application/pro_eng',
- 'ps' => 'application/postscript',
- 'qt' => 'video/quicktime',
- 'ra' => 'audio/x-realaudio',
- 'ram' => 'audio/x-pn-realaudio',
- 'ras' => 'image/cmu-raster',
- 'rgb' => 'image/x-rgb',
- 'rm' => 'audio/x-pn-realaudio',
- 'roff' => 'application/x-troff',
- 'rpm' => 'audio/x-pn-realaudio-plugin',
- 'rtf' => 'text/rtf',
- 'rtx' => 'text/richtext',
- 'scm' => 'application/x-lotusscreencam',
- 'set' => 'application/set',
- 'sgm' => 'text/sgml',
- 'sgml' => 'text/sgml',
- 'sh' => 'application/x-sh',
- 'shar' => 'application/x-shar',
- 'silo' => 'model/mesh',
- 'sit' => 'application/x-stuffit',
- 'skd' => 'application/x-koan',
- 'skm' => 'application/x-koan',
- 'skp' => 'application/x-koan',
- 'skt' => 'application/x-koan',
- 'smi' => 'application/smil',
- 'smil' => 'application/smil',
- 'snd' => 'audio/basic',
- 'sol' => 'application/solids',
- 'spl' => 'application/x-futuresplash',
- 'spx' => 'audio/ogg',
- 'src' => 'application/x-wais-source',
- 'step' => 'application/STEP',
- 'stl' => 'application/SLA',
- 'stp' => 'application/STEP',
- 'sv4cpio' => 'application/x-sv4cpio',
- 'sv4crc' => 'application/x-sv4crc',
- 'swf' => 'application/x-shockwave-flash',
- 't' => 'application/x-troff',
- 'tar' => 'application/x-tar',
- 'tcl' => 'application/x-tcl',
- 'tex' => 'application/x-tex',
- 'texi' => 'application/x-texinfo',
- 'texinfo' => 'application/x-texinfo',
- 'tif' => 'image/tiff',
- 'tiff' => 'image/tiff',
- 'tr' => 'application/x-troff',
- 'tsi' => 'audio/TSP-audio',
- 'tsp' => 'application/dsptype',
- 'tsv' => 'text/tab-separated-values',
- 'txt' => 'text/plain',
- 'unv' => 'application/i-deas',
- 'ustar' => 'application/x-ustar',
- 'vcd' => 'application/x-cdlink',
- 'vda' => 'application/vda',
- 'viv' => 'video/vnd.vivo',
- 'vivo' => 'video/vnd.vivo',
- 'vrml' => 'model/vrml',
- 'wav' => 'audio/x-wav',
- 'wrl' => 'model/vrml',
- 'xbm' => 'image/x-xbitmap',
- 'xlc' => 'application/vnd.ms-excel',
- 'xll' => 'application/vnd.ms-excel',
- 'xlm' => 'application/vnd.ms-excel',
- 'xls' => 'application/vnd.ms-excel',
- 'xlw' => 'application/vnd.ms-excel',
- 'xml' => 'application/xml',
- 'xpm' => 'image/x-xpixmap',
- 'xspf' => 'application/xspf+xml',
- 'xwd' => 'image/x-xwindowdump',
- 'xyz' => 'chemical/x-pdb',
- 'zip' => 'application/zip',
-);
diff --git a/framework/yii/helpers/mimeTypes.php b/framework/yii/helpers/mimeTypes.php
new file mode 100644
index 0000000..4e9f61a
--- /dev/null
+++ b/framework/yii/helpers/mimeTypes.php
@@ -0,0 +1,187 @@
+ 'application/postscript',
+ 'aif' => 'audio/x-aiff',
+ 'aifc' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'anx' => 'application/annodex',
+ 'asc' => 'text/plain',
+ 'au' => 'audio/basic',
+ 'avi' => 'video/x-msvideo',
+ 'axa' => 'audio/annodex',
+ 'axv' => 'video/annodex',
+ 'bcpio' => 'application/x-bcpio',
+ 'bin' => 'application/octet-stream',
+ 'bmp' => 'image/bmp',
+ 'c' => 'text/plain',
+ 'cc' => 'text/plain',
+ 'ccad' => 'application/clariscad',
+ 'cdf' => 'application/x-netcdf',
+ 'class' => 'application/octet-stream',
+ 'cpio' => 'application/x-cpio',
+ 'cpt' => 'application/mac-compactpro',
+ 'csh' => 'application/x-csh',
+ 'css' => 'text/css',
+ 'dcr' => 'application/x-director',
+ 'dir' => 'application/x-director',
+ 'dms' => 'application/octet-stream',
+ 'doc' => 'application/msword',
+ 'drw' => 'application/drafting',
+ 'dvi' => 'application/x-dvi',
+ 'dwg' => 'application/acad',
+ 'dxf' => 'application/dxf',
+ 'dxr' => 'application/x-director',
+ 'eps' => 'application/postscript',
+ 'etx' => 'text/x-setext',
+ 'exe' => 'application/octet-stream',
+ 'ez' => 'application/andrew-inset',
+ 'f' => 'text/plain',
+ 'f90' => 'text/plain',
+ 'flac' => 'audio/flac',
+ 'fli' => 'video/x-fli',
+ 'flv' => 'video/x-flv',
+ 'gif' => 'image/gif',
+ 'gtar' => 'application/x-gtar',
+ 'gz' => 'application/x-gzip',
+ 'h' => 'text/plain',
+ 'hdf' => 'application/x-hdf',
+ 'hh' => 'text/plain',
+ 'hqx' => 'application/mac-binhex40',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'ice' => 'x-conference/x-cooltalk',
+ 'ief' => 'image/ief',
+ 'iges' => 'model/iges',
+ 'igs' => 'model/iges',
+ 'ips' => 'application/x-ipscript',
+ 'ipx' => 'application/x-ipix',
+ 'jpe' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'js' => 'application/x-javascript',
+ 'kar' => 'audio/midi',
+ 'latex' => 'application/x-latex',
+ 'lha' => 'application/octet-stream',
+ 'lsp' => 'application/x-lisp',
+ 'lzh' => 'application/octet-stream',
+ 'm' => 'text/plain',
+ 'man' => 'application/x-troff-man',
+ 'me' => 'application/x-troff-me',
+ 'mesh' => 'model/mesh',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mif' => 'application/vnd.mif',
+ 'mime' => 'www/mime',
+ 'mov' => 'video/quicktime',
+ 'movie' => 'video/x-sgi-movie',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg',
+ 'mpe' => 'video/mpeg',
+ 'mpeg' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mpga' => 'audio/mpeg',
+ 'ms' => 'application/x-troff-ms',
+ 'msh' => 'model/mesh',
+ 'nc' => 'application/x-netcdf',
+ 'oga' => 'audio/ogg',
+ 'ogg' => 'audio/ogg',
+ 'ogv' => 'video/ogg',
+ 'ogx' => 'application/ogg',
+ 'oda' => 'application/oda',
+ 'pbm' => 'image/x-portable-bitmap',
+ 'pdb' => 'chemical/x-pdb',
+ 'pdf' => 'application/pdf',
+ 'pgm' => 'image/x-portable-graymap',
+ 'pgn' => 'application/x-chess-pgn',
+ 'png' => 'image/png',
+ 'pnm' => 'image/x-portable-anymap',
+ 'pot' => 'application/mspowerpoint',
+ 'ppm' => 'image/x-portable-pixmap',
+ 'pps' => 'application/mspowerpoint',
+ 'ppt' => 'application/mspowerpoint',
+ 'ppz' => 'application/mspowerpoint',
+ 'pre' => 'application/x-freelance',
+ 'prt' => 'application/pro_eng',
+ 'ps' => 'application/postscript',
+ 'qt' => 'video/quicktime',
+ 'ra' => 'audio/x-realaudio',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'ras' => 'image/cmu-raster',
+ 'rgb' => 'image/x-rgb',
+ 'rm' => 'audio/x-pn-realaudio',
+ 'roff' => 'application/x-troff',
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
+ 'rtf' => 'text/rtf',
+ 'rtx' => 'text/richtext',
+ 'scm' => 'application/x-lotusscreencam',
+ 'set' => 'application/set',
+ 'sgm' => 'text/sgml',
+ 'sgml' => 'text/sgml',
+ 'sh' => 'application/x-sh',
+ 'shar' => 'application/x-shar',
+ 'silo' => 'model/mesh',
+ 'sit' => 'application/x-stuffit',
+ 'skd' => 'application/x-koan',
+ 'skm' => 'application/x-koan',
+ 'skp' => 'application/x-koan',
+ 'skt' => 'application/x-koan',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'snd' => 'audio/basic',
+ 'sol' => 'application/solids',
+ 'spl' => 'application/x-futuresplash',
+ 'spx' => 'audio/ogg',
+ 'src' => 'application/x-wais-source',
+ 'step' => 'application/STEP',
+ 'stl' => 'application/SLA',
+ 'stp' => 'application/STEP',
+ 'sv4cpio' => 'application/x-sv4cpio',
+ 'sv4crc' => 'application/x-sv4crc',
+ 'swf' => 'application/x-shockwave-flash',
+ 't' => 'application/x-troff',
+ 'tar' => 'application/x-tar',
+ 'tcl' => 'application/x-tcl',
+ 'tex' => 'application/x-tex',
+ 'texi' => 'application/x-texinfo',
+ 'texinfo' => 'application/x-texinfo',
+ 'tif' => 'image/tiff',
+ 'tiff' => 'image/tiff',
+ 'tr' => 'application/x-troff',
+ 'tsi' => 'audio/TSP-audio',
+ 'tsp' => 'application/dsptype',
+ 'tsv' => 'text/tab-separated-values',
+ 'txt' => 'text/plain',
+ 'unv' => 'application/i-deas',
+ 'ustar' => 'application/x-ustar',
+ 'vcd' => 'application/x-cdlink',
+ 'vda' => 'application/vda',
+ 'viv' => 'video/vnd.vivo',
+ 'vivo' => 'video/vnd.vivo',
+ 'vrml' => 'model/vrml',
+ 'wav' => 'audio/x-wav',
+ 'wrl' => 'model/vrml',
+ 'xbm' => 'image/x-xbitmap',
+ 'xlc' => 'application/vnd.ms-excel',
+ 'xll' => 'application/vnd.ms-excel',
+ 'xlm' => 'application/vnd.ms-excel',
+ 'xls' => 'application/vnd.ms-excel',
+ 'xlw' => 'application/vnd.ms-excel',
+ 'xml' => 'application/xml',
+ 'xpm' => 'image/x-xpixmap',
+ 'xspf' => 'application/xspf+xml',
+ 'xwd' => 'image/x-xwindowdump',
+ 'xyz' => 'chemical/x-pdb',
+ 'zip' => 'application/zip',
+];
diff --git a/framework/yii/i18n/DbMessageSource.php b/framework/yii/i18n/DbMessageSource.php
index 14cf0b9..b660a86 100644
--- a/framework/yii/i18n/DbMessageSource.php
+++ b/framework/yii/i18n/DbMessageSource.php
@@ -121,11 +121,11 @@ class DbMessageSource extends MessageSource
protected function loadMessages($category, $language)
{
if ($this->enableCaching) {
- $key = array(
+ $key = [
__CLASS__,
$category,
$language,
- );
+ ];
$messages = $this->cache->get($key);
if ($messages === false) {
$messages = $this->loadMessagesFromDb($category, $language);
@@ -147,10 +147,10 @@ class DbMessageSource extends MessageSource
protected function loadMessagesFromDb($category, $language)
{
$query = new Query();
- $messages = $query->select(array('t1.message message', 't2.translation translation'))
- ->from(array($this->sourceMessageTable . ' t1', $this->messageTable . ' t2'))
+ $messages = $query->select(['t1.message message', 't2.translation translation'])
+ ->from([$this->sourceMessageTable . ' t1', $this->messageTable . ' t2'])
->where('t1.id = t2.id AND t1.category = :category AND t2.language = :language')
- ->params(array(':category' => $category, ':language' => $language))
+ ->params([':category' => $category, ':language' => $language])
->createCommand($this->db)
->queryAll();
return ArrayHelper::map($messages, 'message', 'translation');
diff --git a/framework/yii/i18n/Formatter.php b/framework/yii/i18n/Formatter.php
index 948e277..0c83290 100644
--- a/framework/yii/i18n/Formatter.php
+++ b/framework/yii/i18n/Formatter.php
@@ -19,6 +19,15 @@ use yii\base\InvalidConfigException;
* Formatter requires the PHP "intl" extension to be installed. Formatter supports localized
* formatting of date, time and numbers, based on the current [[locale]].
*
+ * This Formatter can replace the `formatter` application component that is configured by default.
+ * To do so, add the following to your application config under `components`:
+ *
+ * ```php
+ * 'formatter' => [
+ * 'class' => 'yii\i18n\Formatter',
+ * ]
+ * ```
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -30,6 +39,15 @@ class Formatter extends \yii\base\Formatter
*/
public $locale;
/**
+ * @var string|\IntlTimeZone|\DateTimeZone the timezone to use for formatting time and date values.
+ * This can be any value that may be passed to [date_default_timezone_set()](http://www.php.net/manual/en/function.date-default-timezone-set.php)
+ * e.g. `UTC`, `Europe/Berlin` or `America/Chicago`.
+ * Refer to the [php manual](http://www.php.net/manual/en/timezones.php) for available timezones.
+ * This can also be an IntlTimeZone or a DateTimeZone object.
+ * If not set, [[\yii\base\Application::timezone]] will be used.
+ */
+ public $timeZone;
+ /**
* @var string the default format string to be used to format a date.
* This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
* It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
@@ -53,7 +71,7 @@ class Formatter extends \yii\base\Formatter
* for the possible options. This property is used by [[createNumberFormatter]] when
* creating a new number formatter to format decimals, currencies, etc.
*/
- public $numberFormatOptions = array();
+ public $numberFormatOptions = [];
/**
* @var string the character displayed as the decimal point when formatting a number.
* If not set, the decimal separator corresponding to [[locale]] will be used.
@@ -75,11 +93,14 @@ class Formatter extends \yii\base\Formatter
public function init()
{
if (!extension_loaded('intl')) {
- throw new InvalidConfigException('The "intl" PHP extension is not install. It is required to format data values in localized formats.');
+ throw new InvalidConfigException('The "intl" PHP extension is not installed. It is required to format data values in localized formats.');
}
if ($this->locale === null) {
$this->locale = Yii::$app->language;
}
+ if ($this->timeZone === null) {
+ $this->timeZone = Yii::$app->timeZone;
+ }
if ($this->decimalSeparator === null || $this->thousandSeparator === null) {
$formatter = new NumberFormatter($this->locale, NumberFormatter::DECIMAL);
if ($this->decimalSeparator === null) {
@@ -93,12 +114,12 @@ class Formatter extends \yii\base\Formatter
parent::init();
}
- private $_dateFormats = array(
+ private $_dateFormats = [
'short' => IntlDateFormatter::SHORT,
'medium' => IntlDateFormatter::MEDIUM,
'long' => IntlDateFormatter::LONG,
'full' => IntlDateFormatter::FULL,
- );
+ ];
/**
* Formats the value as a date.
@@ -116,19 +137,28 @@ class Formatter extends \yii\base\Formatter
* It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
*
* @return string the formatted result
+ * @throws InvalidConfigException when formatting fails due to invalid parameters.
* @see dateFormat
*/
public function asDate($value, $format = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
$value = $this->normalizeDatetimeValue($value);
if ($format === null) {
$format = $this->dateFormat;
}
if (isset($this->_dateFormats[$format])) {
- $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], IntlDateFormatter::NONE);
+ $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], IntlDateFormatter::NONE, $this->timeZone);
} else {
- $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE);
- $formatter->setPattern($format);
+ $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $this->timeZone);
+ if ($formatter !== null) {
+ $formatter->setPattern($format);
+ }
+ }
+ if ($formatter === null) {
+ throw new InvalidConfigException(intl_get_error_message());
}
return $formatter->format($value);
}
@@ -149,19 +179,28 @@ class Formatter extends \yii\base\Formatter
* It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
*
* @return string the formatted result
+ * @throws InvalidConfigException when formatting fails due to invalid parameters.
* @see timeFormat
*/
public function asTime($value, $format = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
$value = $this->normalizeDatetimeValue($value);
if ($format === null) {
$format = $this->timeFormat;
}
if (isset($this->_dateFormats[$format])) {
- $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, $this->_dateFormats[$format]);
+ $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, $this->_dateFormats[$format], $this->timeZone);
} else {
- $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE);
- $formatter->setPattern($format);
+ $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $this->timeZone);
+ if ($formatter !== null) {
+ $formatter->setPattern($format);
+ }
+ }
+ if ($formatter === null) {
+ throw new InvalidConfigException(intl_get_error_message());
}
return $formatter->format($value);
}
@@ -182,19 +221,28 @@ class Formatter extends \yii\base\Formatter
* It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
*
* @return string the formatted result
+ * @throws InvalidConfigException when formatting fails due to invalid parameters.
* @see datetimeFormat
*/
public function asDatetime($value, $format = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
$value = $this->normalizeDatetimeValue($value);
if ($format === null) {
$format = $this->datetimeFormat;
}
if (isset($this->_dateFormats[$format])) {
- $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], $this->_dateFormats[$format]);
+ $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], $this->_dateFormats[$format], $this->timeZone);
} else {
- $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE);
- $formatter->setPattern($format);
+ $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $this->timeZone);
+ if ($formatter !== null) {
+ $formatter->setPattern($format);
+ }
+ }
+ if ($formatter === null) {
+ throw new InvalidConfigException(intl_get_error_message());
}
return $formatter->format($value);
}
@@ -208,6 +256,9 @@ class Formatter extends \yii\base\Formatter
*/
public function asDecimal($value, $format = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return $this->createNumberFormatter(NumberFormatter::DECIMAL, $format)->format($value);
}
@@ -221,6 +272,9 @@ class Formatter extends \yii\base\Formatter
*/
public function asCurrency($value, $currency = 'USD', $format = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return $this->createNumberFormatter(NumberFormatter::CURRENCY, $format)->formatCurrency($value, $currency);
}
@@ -233,6 +287,9 @@ class Formatter extends \yii\base\Formatter
*/
public function asPercent($value, $format = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return $this->createNumberFormatter(NumberFormatter::PERCENT, $format)->format($value);
}
@@ -245,6 +302,9 @@ class Formatter extends \yii\base\Formatter
*/
public function asScientific($value, $format = null)
{
+ if ($value === null) {
+ return $this->nullDisplay;
+ }
return $this->createNumberFormatter(NumberFormatter::SCIENTIFIC, $format)->format($value);
}
diff --git a/framework/yii/i18n/GettextMessageSource.php b/framework/yii/i18n/GettextMessageSource.php
index 5e29487..66704c4 100644
--- a/framework/yii/i18n/GettextMessageSource.php
+++ b/framework/yii/i18n/GettextMessageSource.php
@@ -9,6 +9,23 @@ namespace yii\i18n;
use Yii;
+/**
+ * GettextMessageSource represents a message source that is based on GNU Gettext.
+ *
+ * Each GettextMessageSource instance represents the message tranlations
+ * for a single domain. And each message category represents a message context
+ * in Gettext. Translated messages are stored as either a MO or PO file,
+ * depending on the [[useMoFile]] property value.
+ *
+ * All translations are saved under the [[basePath]] directory.
+ *
+ * Translations in one language are kept as MO or PO files under an individual
+ * subdirectory whose name is the language ID. The file name is specified via
+ * [[catalog]] property, which defaults to 'messages'.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
class GettextMessageSource extends MessageSource
{
const MO_FILE_EXT = '.mo';
@@ -51,18 +68,18 @@ class GettextMessageSource extends MessageSource
if (is_file($messageFile)) {
if ($this->useMoFile) {
- $gettextFile = new GettextMoFile(array('useBigEndian' => $this->useBigEndian));
+ $gettextFile = new GettextMoFile(['useBigEndian' => $this->useBigEndian]);
} else {
$gettextFile = new GettextPoFile();
}
$messages = $gettextFile->load($messageFile, $category);
if (!is_array($messages)) {
- $messages = array();
+ $messages = [];
}
return $messages;
} else {
Yii::error("The message file for category '$category' does not exist: $messageFile", __METHOD__);
- return array();
+ return [];
}
}
}
diff --git a/framework/yii/i18n/GettextMoFile.php b/framework/yii/i18n/GettextMoFile.php
index c12aebe..b4a016d 100644
--- a/framework/yii/i18n/GettextMoFile.php
+++ b/framework/yii/i18n/GettextMoFile.php
@@ -54,6 +54,7 @@ class GettextMoFile extends GettextFile
* @param string $context message context
* @return array message translations. Array keys are source messages and array values are translated messages:
* source message => translated message.
+ * @throws Exception if unable to read the MO file
*/
public function load($filePath, $context)
{
@@ -85,23 +86,23 @@ class GettextMoFile extends GettextFile
$sourceOffset = $this->readInteger($fileHandle);
$targetOffset = $this->readInteger($fileHandle);
- $sourceLengths = array();
- $sourceOffsets = array();
+ $sourceLengths = [];
+ $sourceOffsets = [];
fseek($fileHandle, $sourceOffset);
for ($i = 0; $i < $count; ++$i) {
$sourceLengths[] = $this->readInteger($fileHandle);
$sourceOffsets[] = $this->readInteger($fileHandle);
}
- $targetLengths = array();
- $targetOffsets = array();
+ $targetLengths = [];
+ $targetOffsets = [];
fseek($fileHandle, $targetOffset);
for ($i = 0; $i < $count; ++$i) {
$targetLengths[] = $this->readInteger($fileHandle);
$targetOffsets[] = $this->readInteger($fileHandle);
}
- $messages = array();
+ $messages = [];
for ($i = 0; $i < $count; ++$i) {
$id = $this->readString($fileHandle, $sourceLengths[$i], $sourceOffsets[$i]);
$separatorPosition = strpos($id, chr(4));
@@ -128,6 +129,7 @@ class GettextMoFile extends GettextFile
* @param array $messages message translations. Array keys are source messages and array values are
* translated messages: source message => translated message. Note if the message has a context,
* the message ID must be prefixed with the context with chr(4) as the separator.
+ * @throws Exception if unable to save the MO file
*/
public function save($filePath, $messages)
{
@@ -203,6 +205,8 @@ class GettextMoFile extends GettextFile
{
if ($byteCount > 0) {
return fread($fileHandle, $byteCount);
+ } else {
+ return null;
}
}
diff --git a/framework/yii/i18n/GettextPoFile.php b/framework/yii/i18n/GettextPoFile.php
index cac075b..8fb618c 100644
--- a/framework/yii/i18n/GettextPoFile.php
+++ b/framework/yii/i18n/GettextPoFile.php
@@ -30,10 +30,10 @@ class GettextPoFile extends GettextFile
. 'msgid\s+((?:".*(?decode($matches[3][$i]);
@@ -74,8 +74,8 @@ class GettextPoFile extends GettextFile
protected function encode($string)
{
return str_replace(
- array('"', "\n", "\t", "\r"),
- array('\\"', '\\n', '\\t', '\\r'),
+ ['"', "\n", "\t", "\r"],
+ ['\\"', '\\n', '\\t', '\\r'],
$string
);
}
@@ -88,8 +88,8 @@ class GettextPoFile extends GettextFile
protected function decode($string)
{
$string = preg_replace(
- array('/"\s+"/', '/\\\\n/', '/\\\\r/', '/\\\\t/', '/\\\\"/'),
- array('', "\n", "\r", "\t", '"'),
+ ['/"\s+"/', '/\\\\n/', '/\\\\r/', '/\\\\t/', '/\\\\"/'],
+ ['', "\n", "\r", "\t", '"'],
$string
);
return substr(rtrim($string), 1, -1);
diff --git a/framework/yii/i18n/I18N.php b/framework/yii/i18n/I18N.php
index b929f49..c59a6d2 100644
--- a/framework/yii/i18n/I18N.php
+++ b/framework/yii/i18n/I18N.php
@@ -10,11 +10,17 @@ namespace yii\i18n;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
-use yii\base\InvalidParamException;
/**
* I18N provides features related with internationalization (I18N) and localization (L10N).
*
+ * I18N is configured as an application component in [[yii\base\Application]] by default.
+ * You can access that instance via `Yii::$app->i18n`.
+ *
+ * @property MessageFormatter $messageFormatter The message formatter to be used to format message via ICU
+ * message format. Note that the type of this property differs in getter and setter. See
+ * [[getMessageFormatter()]] and [[setMessageFormatter()]] for details.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -37,17 +43,6 @@ class I18N extends Component
* You may override the configuration of both categories.
*/
public $translations;
- /**
- * @var string the path or path alias of the file that contains the plural rules.
- * By default, this refers to a file shipped with the Yii distribution. The file is obtained
- * by converting from the data file in the CLDR project.
- *
- * If the default rule file does not contain the expected rules, you may copy and modify it
- * for your application, and then configure this property to point to your modified copy.
- *
- * @see http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html
- */
- public $pluralRuleFile = '@yii/i18n/data/plurals.php';
/**
* Initializes the component by configuring the default message categories.
@@ -56,48 +51,100 @@ class I18N extends Component
{
parent::init();
if (!isset($this->translations['yii'])) {
- $this->translations['yii'] = array(
+ $this->translations['yii'] = [
'class' => 'yii\i18n\PhpMessageSource',
- 'sourceLanguage' => 'en_US',
+ 'sourceLanguage' => 'en-US',
'basePath' => '@yii/messages',
- );
+ ];
}
if (!isset($this->translations['app'])) {
- $this->translations['app'] = array(
+ $this->translations['app'] = [
'class' => 'yii\i18n\PhpMessageSource',
- 'sourceLanguage' => 'en_US',
+ 'sourceLanguage' => 'en-US',
'basePath' => '@app/messages',
- );
+ ];
}
}
/**
* Translates a message to the specified language.
- * If the first parameter in `$params` is a number and it is indexed by 0, appropriate plural rules
- * will be applied to the translated message.
+ *
+ * After translation the message will be formatted using [[MessageFormatter]] if it contains
+ * ICU message format and `$params` are not empty.
+ *
* @param string $category the message category.
* @param string $message the message to be translated.
* @param array $params the parameters that will be used to replace the corresponding placeholders in the message.
- * @param string $language the language code (e.g. `en_US`, `en`).
- * @return string the translated message.
+ * @param string $language the language code (e.g. `en-US`, `en`).
+ * @return string the translated and formatted message.
*/
public function translate($category, $message, $params, $language)
{
$message = $this->getMessageSource($category)->translate($category, $message, $language);
+ return $this->format($message, $params, $language);
+ }
- if (!is_array($params)) {
- $params = array($params);
+ /**
+ * Formats a message using [[MessageFormatter]].
+ *
+ * @param string $message the message to be formatted.
+ * @param array $params the parameters that will be used to replace the corresponding placeholders in the message.
+ * @param string $language the language code (e.g. `en-US`, `en`).
+ * @return string the formatted message.
+ */
+ public function format($message, $params, $language)
+ {
+ $params = (array)$params;
+ if ($params === []) {
+ return $message;
}
- if (isset($params[0])) {
- $message = $this->applyPluralRules($message, $params[0], $language);
- if (!isset($params['{n}'])) {
- $params['{n}'] = $params[0];
+ if (preg_match('~{\s*[\d\w]+\s*,~u', $message)) {
+ $formatter = $this->getMessageFormatter();
+ $result = $formatter->format($message, $params, $language);
+ if ($result === false) {
+ $errorMessage = $formatter->getErrorMessage();
+ Yii::warning("Formatting message for language '$language' failed with error: $errorMessage. The message being formatted was: $message.");
+ return $message;
+ } else {
+ return $result;
}
- unset($params[0]);
}
- return empty($params) ? $message : strtr($message, $params);
+ $p = [];
+ foreach($params as $name => $value) {
+ $p['{' . $name . '}'] = $value;
+ }
+ return strtr($message, $p);
+ }
+
+ /**
+ * @var string|array|MessageFormatter
+ */
+ private $_messageFormatter;
+
+ /**
+ * Returns the message formatter instance.
+ * @return MessageFormatter the message formatter to be used to format message via ICU message format.
+ */
+ public function getMessageFormatter()
+ {
+ if ($this->_messageFormatter === null) {
+ $this->_messageFormatter = new MessageFormatter();
+ } elseif (is_array($this->_messageFormatter) || is_string($this->_messageFormatter)) {
+ $this->_messageFormatter = Yii::createObject($this->_messageFormatter);
+ }
+ return $this->_messageFormatter;
+ }
+
+ /**
+ * @param string|array|MessageFormatter $value the message formatter to be used to format message via ICU message format.
+ * Can be given as array or string configuration that will be given to [[Yii::createObject]] to create an instance
+ * or a [[MessageFormatter]] instance.
+ */
+ public function setMessageFormatter($value)
+ {
+ $this->_messageFormatter = $value;
}
/**
@@ -113,7 +160,7 @@ class I18N extends Component
} else {
// try wildcard matching
foreach ($this->translations as $pattern => $config) {
- if (substr($pattern, -1) === '*' && strpos($category, rtrim($pattern, '*')) === 0) {
+ if ($pattern === '*' || substr($pattern, -1) === '*' && strpos($category, rtrim($pattern, '*')) === 0) {
$source = $config;
break;
}
@@ -125,62 +172,4 @@ class I18N extends Component
throw new InvalidConfigException("Unable to locate message source for category '$category'.");
}
}
-
- /**
- * Applies appropriate plural rules to the given message.
- * @param string $message the message to be applied with plural rules
- * @param mixed $number the number by which plural rules will be applied
- * @param string $language the language code that determines which set of plural rules to be applied.
- * @return string the message that has applied plural rules
- */
- protected function applyPluralRules($message, $number, $language)
- {
- if (strpos($message, '|') === false) {
- return $message;
- }
- $chunks = explode('|', $message);
-
- $rules = $this->getPluralRules($language);
- foreach ($rules as $i => $rule) {
- if (isset($chunks[$i]) && $this->evaluate($rule, $number)) {
- return $chunks[$i];
- }
- }
- $n = count($rules);
- return isset($chunks[$n]) ? $chunks[$n] : $chunks[0];
- }
-
- private $_pluralRules = array(); // language => rule set
-
- /**
- * Returns the plural rules for the given language code.
- * @param string $language the language code (e.g. `en_US`, `en`).
- * @return array the plural rules
- * @throws InvalidParamException if the language code is invalid.
- */
- protected function getPluralRules($language)
- {
- if (isset($this->_pluralRules[$language])) {
- return $this->_pluralRules;
- }
- $allRules = require(Yii::getAlias($this->pluralRuleFile));
- if (isset($allRules[$language])) {
- return $this->_pluralRules[$language] = $allRules[$language];
- } elseif (preg_match('/^[a-z]+/', strtolower($language), $matches)) {
- return $this->_pluralRules[$language] = isset($allRules[$matches[0]]) ? $allRules[$matches[0]] : array();
- } else {
- throw new InvalidParamException("Invalid language code: $language");
- }
- }
-
- /**
- * Evaluates a PHP expression with the given number value.
- * @param string $expression the PHP expression
- * @param mixed $n the number value
- * @return boolean the expression result
- */
- protected function evaluate($expression, $n)
- {
- return eval("return $expression;");
- }
}
diff --git a/framework/yii/i18n/MessageFormatter.php b/framework/yii/i18n/MessageFormatter.php
new file mode 100644
index 0000000..43aebdd
--- /dev/null
+++ b/framework/yii/i18n/MessageFormatter.php
@@ -0,0 +1,396 @@
+
+ * @author Carsten Brandt
+ * @since 2.0
+ */
+class MessageFormatter extends Component
+{
+ private $_errorCode = 0;
+ private $_errorMessage = '';
+
+ /**
+ * Get the error code from the last operation
+ * @link http://php.net/manual/en/messageformatter.geterrorcode.php
+ * @return string Code of the last error.
+ */
+ public function getErrorCode()
+ {
+ return $this->_errorCode;
+ }
+
+ /**
+ * Get the error text from the last operation
+ * @link http://php.net/manual/en/messageformatter.geterrormessage.php
+ * @return string Description of the last error.
+ */
+ public function getErrorMessage()
+ {
+ return $this->_errorMessage;
+ }
+
+ /**
+ * Formats a message via [ICU message format](http://userguide.icu-project.org/formatparse/messages)
+ *
+ * It uses the PHP intl extension's [MessageFormatter](http://www.php.net/manual/en/class.messageformatter.php)
+ * and works around some issues.
+ * If PHP intl is not installed a fallback will be used that supports a subset of the ICU message format.
+ *
+ * @param string $pattern The pattern string to insert parameters into.
+ * @param array $params The array of name value pairs to insert into the format string.
+ * @param string $language The locale to use for formatting locale-dependent parts
+ * @return string|boolean The formatted pattern string or `FALSE` if an error occurred
+ */
+ public function format($pattern, $params, $language)
+ {
+ $this->_errorCode = 0;
+ $this->_errorMessage = '';
+
+ if ($params === []) {
+ return $pattern;
+ }
+
+ if (!class_exists('MessageFormatter', false)) {
+ return $this->fallbackFormat($pattern, $params, $language);
+ }
+
+ if (version_compare(PHP_VERSION, '5.5.0', '<') || version_compare(INTL_ICU_VERSION, '4.8', '<')) {
+ // replace named arguments
+ $pattern = $this->replaceNamedArguments($pattern, $params, $newParams);
+ $params = $newParams;
+ }
+
+ $formatter = new \MessageFormatter($language, $pattern);
+ if ($formatter === null) {
+ $this->_errorCode = intl_get_error_code();
+ $this->_errorMessage = "Message pattern is invalid: " . intl_get_error_message();
+ return false;
+ }
+ $result = $formatter->format($params);
+ if ($result === false) {
+ $this->_errorCode = $formatter->getErrorCode();
+ $this->_errorMessage = $formatter->getErrorMessage();
+ return false;
+ } else {
+ return $result;
+ }
+ }
+
+ /**
+ * Parses an input string according to an [ICU message format](http://userguide.icu-project.org/formatparse/messages) pattern.
+ *
+ * It uses the PHP intl extension's [MessageFormatter::parse()](http://www.php.net/manual/en/messageformatter.parsemessage.php)
+ * and adds support for named arguments.
+ * Usage of this method requires PHP intl extension to be installed.
+ *
+ * @param string $pattern The pattern to use for parsing the message.
+ * @param string $message The message to parse, conforming to the pattern.
+ * @param string $language The locale to use for formatting locale-dependent parts
+ * @return array|boolean An array containing items extracted, or `FALSE` on error.
+ * @throws \yii\base\NotSupportedException when PHP intl extension is not installed.
+ */
+ public function parse($pattern, $message, $language)
+ {
+ $this->_errorCode = 0;
+ $this->_errorMessage = '';
+
+ if (!class_exists('MessageFormatter', false)) {
+ throw new NotSupportedException('You have to install PHP intl extension to use this feature.');
+ }
+
+ // replace named arguments
+ if (($tokens = $this->tokenizePattern($pattern)) === false) {
+ $this->_errorCode = -1;
+ $this->_errorMessage = "Message pattern is invalid.";
+ return false;
+ }
+ $map = [];
+ foreach($tokens as $i => $token) {
+ if (is_array($token)) {
+ $param = trim($token[0]);
+ if (!isset($map[$param])) {
+ $map[$param] = count($map);
+ }
+ $token[0] = $map[$param];
+ $tokens[$i] = '{' . implode(',', $token) . '}';
+ }
+ }
+ $pattern = implode('', $tokens);
+ $map = array_flip($map);
+
+ $formatter = new \MessageFormatter($language, $pattern);
+ if ($formatter === null) {
+ $this->_errorCode = -1;
+ $this->_errorMessage = "Message pattern is invalid.";
+ return false;
+ }
+ $result = $formatter->parse($message);
+ if ($result === false) {
+ $this->_errorCode = $formatter->getErrorCode();
+ $this->_errorMessage = $formatter->getErrorMessage();
+ return false;
+ } else {
+ $values = [];
+ foreach($result as $key => $value) {
+ $values[$map[$key]] = $value;
+ }
+ return $values;
+ }
+ }
+
+ /**
+ * Replace named placeholders with numeric placeholders and quote unused.
+ *
+ * @param string $pattern The pattern string to replace things into.
+ * @param array $args The array of values to insert into the format string.
+ * @return string The pattern string with placeholders replaced.
+ */
+ private function replaceNamedArguments($pattern, $givenParams, &$resultingParams, &$map = [])
+ {
+ if (($tokens = $this->tokenizePattern($pattern)) === false) {
+ return false;
+ }
+ foreach($tokens as $i => $token) {
+ if (!is_array($token)) {
+ continue;
+ }
+ $param = trim($token[0]);
+ if (isset($givenParams[$param])) {
+ // if param is given, replace it with a number
+ if (!isset($map[$param])) {
+ $map[$param] = count($map);
+ // make sure only used params are passed to format method
+ $resultingParams[$map[$param]] = $givenParams[$param];
+ }
+ $token[0] = $map[$param];
+ $quote = "";
+ } else {
+ // quote unused token
+ $quote = "'";
+ }
+ $type = isset($token[1]) ? trim($token[1]) : 'none';
+ // replace plural and select format recursively
+ if ($type == 'plural' || $type == 'select') {
+ if (!isset($token[2])) {
+ return false;
+ }
+ $subtokens = $this->tokenizePattern($token[2]);
+ $c = count($subtokens);
+ for ($k = 0; $k + 1 < $c; $k++) {
+ if (is_array($subtokens[$k]) || !is_array($subtokens[++$k])) {
+ return false;
+ }
+ $subpattern = $this->replaceNamedArguments(implode(',', $subtokens[$k]), $givenParams, $resultingParams, $map);
+ $subtokens[$k] = $quote . '{' . $quote . $subpattern . $quote . '}' . $quote;
+ }
+ $token[2] = implode('', $subtokens);
+ }
+ $tokens[$i] = $quote . '{' . $quote . implode(',', $token) . $quote . '}' . $quote;
+ }
+ return implode('', $tokens);
+ }
+
+ /**
+ * Fallback implementation for MessageFormatter::formatMessage
+ * @param string $pattern The pattern string to insert things into.
+ * @param array $args The array of values to insert into the format string
+ * @param string $locale The locale to use for formatting locale-dependent parts
+ * @return string|boolean The formatted pattern string or `FALSE` if an error occurred
+ */
+ protected function fallbackFormat($pattern, $args, $locale)
+ {
+ if (($tokens = $this->tokenizePattern($pattern)) === false) {
+ $this->_errorCode = -1;
+ $this->_errorMessage = "Message pattern is invalid.";
+ return false;
+ }
+ foreach($tokens as $i => $token) {
+ if (is_array($token)) {
+ if (($tokens[$i] = $this->parseToken($token, $args, $locale)) === false) {
+ $this->_errorCode = -1;
+ $this->_errorMessage = "Message pattern is invalid.";
+ return false;
+ }
+ }
+ }
+ return implode('', $tokens);
+ }
+
+ /**
+ * Tokenizes a pattern by separating normal text from replaceable patterns
+ * @param string $pattern patter to tokenize
+ * @return array|bool array of tokens or false on failure
+ */
+ private function tokenizePattern($pattern)
+ {
+ $depth = 1;
+ if (($start = $pos = mb_strpos($pattern, '{')) === false) {
+ return [$pattern];
+ }
+ $tokens = [mb_substr($pattern, 0, $pos)];
+ while (true) {
+ $open = mb_strpos($pattern, '{', $pos + 1);
+ $close = mb_strpos($pattern, '}', $pos + 1);
+ if ($open === false && $close === false) {
+ break;
+ }
+ if ($open === false) {
+ $open = mb_strlen($pattern);
+ }
+ if ($close > $open) {
+ $depth++;
+ $pos = $open;
+ } else {
+ $depth--;
+ $pos = $close;
+ }
+ if ($depth == 0) {
+ $tokens[] = explode(',', mb_substr($pattern, $start + 1, $pos - $start - 1), 3);
+ $start = $pos + 1;
+ $tokens[] = mb_substr($pattern, $start, $open - $start);
+ $start = $open;
+ }
+ }
+ if ($depth != 0) {
+ return false;
+ }
+ return $tokens;
+ }
+
+ /**
+ * Parses a token
+ * @param array $token the token to parse
+ * @param array $args arguments to replace
+ * @param string $locale the locale
+ * @return bool|string parsed token or false on failure
+ * @throws \yii\base\NotSupportedException when unsupported formatting is used.
+ */
+ private function parseToken($token, $args, $locale)
+ {
+ // parsing pattern based on ICU grammar:
+ // http://icu-project.org/apiref/icu4c/classMessageFormat.html#details
+
+ $param = trim($token[0]);
+ if (isset($args[$param])) {
+ $arg = $args[$param];
+ } else {
+ return '{' . implode(',', $token) . '}';
+ }
+ $type = isset($token[1]) ? trim($token[1]) : 'none';
+ switch($type)
+ {
+ case 'date':
+ case 'time':
+ case 'spellout':
+ case 'ordinal':
+ case 'duration':
+ case 'choice':
+ case 'selectordinal':
+ throw new NotSupportedException("Message format '$type' is not supported. You have to install PHP intl extension to use this feature.");
+ case 'number':
+ if (is_int($arg) && (!isset($token[2]) || trim($token[2]) == 'integer')) {
+ return $arg;
+ }
+ throw new NotSupportedException("Message format 'number' is only supported for integer values. You have to install PHP intl extension to use this feature.");
+ case 'none': return $arg;
+ case 'select':
+ /* http://icu-project.org/apiref/icu4c/classicu_1_1SelectFormat.html
+ selectStyle = (selector '{' message '}')+
+ */
+ if (!isset($token[2])) {
+ return false;
+ }
+ $select = static::tokenizePattern($token[2]);
+ $c = count($select);
+ $message = false;
+ for ($i = 0; $i + 1 < $c; $i++) {
+ if (is_array($select[$i]) || !is_array($select[$i + 1])) {
+ return false;
+ }
+ $selector = trim($select[$i++]);
+ if ($message === false && $selector == 'other' || $selector == $arg) {
+ $message = implode(',', $select[$i]);
+ }
+ }
+ if ($message !== false) {
+ return $this->fallbackFormat($message, $args, $locale);
+ }
+ break;
+ case 'plural':
+ /* http://icu-project.org/apiref/icu4c/classicu_1_1PluralFormat.html
+ pluralStyle = [offsetValue] (selector '{' message '}')+
+ offsetValue = "offset:" number
+ selector = explicitValue | keyword
+ explicitValue = '=' number // adjacent, no white space in between
+ keyword = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+
+ message: see MessageFormat
+ */
+ if (!isset($token[2])) {
+ return false;
+ }
+ $plural = static::tokenizePattern($token[2]);
+ $c = count($plural);
+ $message = false;
+ $offset = 0;
+ for ($i = 0; $i + 1 < $c; $i++) {
+ if (is_array($plural[$i]) || !is_array($plural[$i + 1])) {
+ return false;
+ }
+ $selector = trim($plural[$i++]);
+ if ($i == 1 && substr($selector, 0, 7) == 'offset:') {
+ $offset = (int) trim(mb_substr($selector, 7, ($pos = mb_strpos(str_replace(["\n", "\r", "\t"], ' ', $selector), ' ', 7)) - 7));
+ $selector = trim(mb_substr($selector, $pos + 1));
+ }
+ if ($message === false && $selector == 'other' ||
+ $selector[0] == '=' && (int) mb_substr($selector, 1) == $arg ||
+ $selector == 'one' && $arg - $offset == 1
+ ) {
+ $message = implode(',', str_replace('#', $arg - $offset, $plural[$i]));
+ }
+ }
+ if ($message !== false) {
+ return $this->fallbackFormat($message, $args, $locale);
+ }
+ break;
+ }
+ return false;
+ }
+}
diff --git a/framework/yii/i18n/MessageSource.php b/framework/yii/i18n/MessageSource.php
index 90adbfb..95f907d 100644
--- a/framework/yii/i18n/MessageSource.php
+++ b/framework/yii/i18n/MessageSource.php
@@ -38,7 +38,7 @@ class MessageSource extends Component
*/
public $sourceLanguage;
- private $_messages = array();
+ private $_messages = [];
/**
* Initializes this component.
@@ -62,7 +62,7 @@ class MessageSource extends Component
*/
protected function loadMessages($category, $language)
{
- return array();
+ return [];
}
/**
@@ -106,11 +106,11 @@ class MessageSource extends Component
if (isset($this->_messages[$key][$message]) && $this->_messages[$key][$message] !== '') {
return $this->_messages[$key][$message];
} elseif ($this->hasEventHandlers('missingTranslation')) {
- $event = new MissingTranslationEvent(array(
+ $event = new MissingTranslationEvent([
'category' => $category,
'message' => $message,
'language' => $language,
- ));
+ ]);
$this->trigger(self::EVENT_MISSING_TRANSLATION, $event);
return $this->_messages[$key] = $event->message;
} else {
diff --git a/framework/yii/i18n/MissingTranslationEvent.php b/framework/yii/i18n/MissingTranslationEvent.php
index 9ac337a..5c8ffd3 100644
--- a/framework/yii/i18n/MissingTranslationEvent.php
+++ b/framework/yii/i18n/MissingTranslationEvent.php
@@ -27,7 +27,7 @@ class MissingTranslationEvent extends Event
*/
public $category;
/**
- * @var string the language ID (e.g. en_US) that the message is to be translated to
+ * @var string the language ID (e.g. en-US) that the message is to be translated to
*/
public $language;
}
diff --git a/framework/yii/i18n/PhpMessageSource.php b/framework/yii/i18n/PhpMessageSource.php
index f62939f..e105083 100644
--- a/framework/yii/i18n/PhpMessageSource.php
+++ b/framework/yii/i18n/PhpMessageSource.php
@@ -20,12 +20,14 @@ use Yii;
* - Within each PHP script, the message translations are returned as an array like the following:
*
* ~~~
- * return array(
+ * return [
* 'original message 1' => 'translated message 1',
* 'original message 2' => 'translated message 2',
- * );
+ * ];
* ~~~
*
+ * You may use [[fileMap]] to customize the association between category names and the file names.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -41,10 +43,10 @@ class PhpMessageSource extends MessageSource
* The file paths are relative to [[basePath]]. For example,
*
* ~~~
- * array(
+ * [
* 'core' => 'core.php',
* 'ext' => 'extensions.php',
- * )
+ * ]
* ~~~
*/
public $fileMap;
@@ -60,20 +62,18 @@ class PhpMessageSource extends MessageSource
$messageFile = Yii::getAlias($this->basePath) . "/$language/";
if (isset($this->fileMap[$category])) {
$messageFile .= $this->fileMap[$category];
- } elseif (($pos = strrpos($category, '\\')) !== false) {
- $messageFile .= (substr($category, $pos) . '.php');
} else {
- $messageFile .= "$category.php";
+ $messageFile .= str_replace('\\', '/', $category) . '.php';
}
if (is_file($messageFile)) {
$messages = include($messageFile);
if (!is_array($messages)) {
- $messages = array();
+ $messages = [];
}
return $messages;
} else {
Yii::error("The message file for category '$category' does not exist: $messageFile", __METHOD__);
- return array();
+ return [];
}
}
}
diff --git a/framework/yii/i18n/data/plurals.php b/framework/yii/i18n/data/plurals.php
deleted file mode 100644
index cb51307..0000000
--- a/framework/yii/i18n/data/plurals.php
+++ /dev/null
@@ -1,627 +0,0 @@
-
- array (
- 0 => '$n==0',
- 1 => '$n==1',
- 2 => '$n==2',
- 3 => 'in_array(fmod($n,100),range(3,10))',
- 4 => 'in_array(fmod($n,100),range(11,99))',
- ),
- 'asa' =>
- array (
- 0 => '$n==1',
- ),
- 'af' =>
- array (
- 0 => '$n==1',
- ),
- 'bem' =>
- array (
- 0 => '$n==1',
- ),
- 'bez' =>
- array (
- 0 => '$n==1',
- ),
- 'bg' =>
- array (
- 0 => '$n==1',
- ),
- 'bn' =>
- array (
- 0 => '$n==1',
- ),
- 'brx' =>
- array (
- 0 => '$n==1',
- ),
- 'ca' =>
- array (
- 0 => '$n==1',
- ),
- 'cgg' =>
- array (
- 0 => '$n==1',
- ),
- 'chr' =>
- array (
- 0 => '$n==1',
- ),
- 'da' =>
- array (
- 0 => '$n==1',
- ),
- 'de' =>
- array (
- 0 => '$n==1',
- ),
- 'dv' =>
- array (
- 0 => '$n==1',
- ),
- 'ee' =>
- array (
- 0 => '$n==1',
- ),
- 'el' =>
- array (
- 0 => '$n==1',
- ),
- 'en' =>
- array (
- 0 => '$n==1',
- ),
- 'eo' =>
- array (
- 0 => '$n==1',
- ),
- 'es' =>
- array (
- 0 => '$n==1',
- ),
- 'et' =>
- array (
- 0 => '$n==1',
- ),
- 'eu' =>
- array (
- 0 => '$n==1',
- ),
- 'fi' =>
- array (
- 0 => '$n==1',
- ),
- 'fo' =>
- array (
- 0 => '$n==1',
- ),
- 'fur' =>
- array (
- 0 => '$n==1',
- ),
- 'fy' =>
- array (
- 0 => '$n==1',
- ),
- 'gl' =>
- array (
- 0 => '$n==1',
- ),
- 'gsw' =>
- array (
- 0 => '$n==1',
- ),
- 'gu' =>
- array (
- 0 => '$n==1',
- ),
- 'ha' =>
- array (
- 0 => '$n==1',
- ),
- 'haw' =>
- array (
- 0 => '$n==1',
- ),
- 'he' =>
- array (
- 0 => '$n==1',
- ),
- 'is' =>
- array (
- 0 => '$n==1',
- ),
- 'it' =>
- array (
- 0 => '$n==1',
- ),
- 'jmc' =>
- array (
- 0 => '$n==1',
- ),
- 'kaj' =>
- array (
- 0 => '$n==1',
- ),
- 'kcg' =>
- array (
- 0 => '$n==1',
- ),
- 'kk' =>
- array (
- 0 => '$n==1',
- ),
- 'kl' =>
- array (
- 0 => '$n==1',
- ),
- 'ksb' =>
- array (
- 0 => '$n==1',
- ),
- 'ku' =>
- array (
- 0 => '$n==1',
- ),
- 'lb' =>
- array (
- 0 => '$n==1',
- ),
- 'lg' =>
- array (
- 0 => '$n==1',
- ),
- 'mas' =>
- array (
- 0 => '$n==1',
- ),
- 'ml' =>
- array (
- 0 => '$n==1',
- ),
- 'mn' =>
- array (
- 0 => '$n==1',
- ),
- 'mr' =>
- array (
- 0 => '$n==1',
- ),
- 'nah' =>
- array (
- 0 => '$n==1',
- ),
- 'nb' =>
- array (
- 0 => '$n==1',
- ),
- 'nd' =>
- array (
- 0 => '$n==1',
- ),
- 'ne' =>
- array (
- 0 => '$n==1',
- ),
- 'nl' =>
- array (
- 0 => '$n==1',
- ),
- 'nn' =>
- array (
- 0 => '$n==1',
- ),
- 'no' =>
- array (
- 0 => '$n==1',
- ),
- 'nr' =>
- array (
- 0 => '$n==1',
- ),
- 'ny' =>
- array (
- 0 => '$n==1',
- ),
- 'nyn' =>
- array (
- 0 => '$n==1',
- ),
- 'om' =>
- array (
- 0 => '$n==1',
- ),
- 'or' =>
- array (
- 0 => '$n==1',
- ),
- 'pa' =>
- array (
- 0 => '$n==1',
- ),
- 'pap' =>
- array (
- 0 => '$n==1',
- ),
- 'ps' =>
- array (
- 0 => '$n==1',
- ),
- 'pt' =>
- array (
- 0 => '$n==1',
- ),
- 'rof' =>
- array (
- 0 => '$n==1',
- ),
- 'rm' =>
- array (
- 0 => '$n==1',
- ),
- 'rwk' =>
- array (
- 0 => '$n==1',
- ),
- 'saq' =>
- array (
- 0 => '$n==1',
- ),
- 'seh' =>
- array (
- 0 => '$n==1',
- ),
- 'sn' =>
- array (
- 0 => '$n==1',
- ),
- 'so' =>
- array (
- 0 => '$n==1',
- ),
- 'sq' =>
- array (
- 0 => '$n==1',
- ),
- 'ss' =>
- array (
- 0 => '$n==1',
- ),
- 'ssy' =>
- array (
- 0 => '$n==1',
- ),
- 'st' =>
- array (
- 0 => '$n==1',
- ),
- 'sv' =>
- array (
- 0 => '$n==1',
- ),
- 'sw' =>
- array (
- 0 => '$n==1',
- ),
- 'syr' =>
- array (
- 0 => '$n==1',
- ),
- 'ta' =>
- array (
- 0 => '$n==1',
- ),
- 'te' =>
- array (
- 0 => '$n==1',
- ),
- 'teo' =>
- array (
- 0 => '$n==1',
- ),
- 'tig' =>
- array (
- 0 => '$n==1',
- ),
- 'tk' =>
- array (
- 0 => '$n==1',
- ),
- 'tn' =>
- array (
- 0 => '$n==1',
- ),
- 'ts' =>
- array (
- 0 => '$n==1',
- ),
- 'ur' =>
- array (
- 0 => '$n==1',
- ),
- 'wae' =>
- array (
- 0 => '$n==1',
- ),
- 've' =>
- array (
- 0 => '$n==1',
- ),
- 'vun' =>
- array (
- 0 => '$n==1',
- ),
- 'xh' =>
- array (
- 0 => '$n==1',
- ),
- 'xog' =>
- array (
- 0 => '$n==1',
- ),
- 'zu' =>
- array (
- 0 => '$n==1',
- ),
- 'ak' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'am' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'bh' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'fil' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'tl' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'guw' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'hi' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'ln' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'mg' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'nso' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'ti' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'wa' =>
- array (
- 0 => '($n==0||$n==1)',
- ),
- 'ff' =>
- array (
- 0 => '($n>=0&&$n<=2)&&$n!=2',
- ),
- 'fr' =>
- array (
- 0 => '($n>=0&&$n<=2)&&$n!=2',
- ),
- 'kab' =>
- array (
- 0 => '($n>=0&&$n<=2)&&$n!=2',
- ),
- 'lv' =>
- array (
- 0 => '$n==0',
- 1 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- ),
- 'iu' =>
- array (
- 0 => '$n==1',
- 1 => '$n==2',
- ),
- 'kw' =>
- array (
- 0 => '$n==1',
- 1 => '$n==2',
- ),
- 'naq' =>
- array (
- 0 => '$n==1',
- 1 => '$n==2',
- ),
- 'se' =>
- array (
- 0 => '$n==1',
- 1 => '$n==2',
- ),
- 'sma' =>
- array (
- 0 => '$n==1',
- 1 => '$n==2',
- ),
- 'smi' =>
- array (
- 0 => '$n==1',
- 1 => '$n==2',
- ),
- 'smj' =>
- array (
- 0 => '$n==1',
- 1 => '$n==2',
- ),
- 'smn' =>
- array (
- 0 => '$n==1',
- 1 => '$n==2',
- ),
- 'sms' =>
- array (
- 0 => '$n==1',
- 1 => '$n==2',
- ),
- 'ga' =>
- array (
- 0 => '$n==1',
- 1 => '$n==2',
- 2 => 'in_array($n,array(3,4,5,6))',
- 3 => 'in_array($n,array(7,8,9,10))',
- ),
- 'ro' =>
- array (
- 0 => '$n==1',
- 1 => '$n==0||$n!=1&&in_array(fmod($n,100),range(1,19))',
- ),
- 'mo' =>
- array (
- 0 => '$n==1',
- 1 => '$n==0||$n!=1&&in_array(fmod($n,100),range(1,19))',
- ),
- 'lt' =>
- array (
- 0 => 'fmod($n,10)==1&&!in_array(fmod($n,100),range(11,19))',
- 1 => 'in_array(fmod($n,10),range(2,9))&&!in_array(fmod($n,100),range(11,19))',
- ),
- 'be' =>
- array (
- 0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
- 2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))',
- ),
- 'bs' =>
- array (
- 0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
- 2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))',
- ),
- 'hr' =>
- array (
- 0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
- 2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))',
- ),
- 'ru' =>
- array (
- 0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
- 2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))',
- ),
- 'sh' =>
- array (
- 0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
- 2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))',
- ),
- 'sr' =>
- array (
- 0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
- 2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))',
- ),
- 'uk' =>
- array (
- 0 => 'fmod($n,10)==1&&fmod($n,100)!=11',
- 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
- 2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))',
- ),
- 'cs' =>
- array (
- 0 => '$n==1',
- 1 => 'in_array($n,array(2,3,4))',
- ),
- 'sk' =>
- array (
- 0 => '$n==1',
- 1 => 'in_array($n,array(2,3,4))',
- ),
- 'pl' =>
- array (
- 0 => '$n==1',
- 1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))',
- 2 => '$n!=1&&in_array(fmod($n,10),array(0,1))||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(12,13,14))',
- ),
- 'sl' =>
- array (
- 0 => 'fmod($n,100)==1',
- 1 => 'fmod($n,100)==2',
- 2 => 'in_array(fmod($n,100),array(3,4))',
- ),
- 'mt' =>
- array (
- 0 => '$n==1',
- 1 => '$n==0||in_array(fmod($n,100),range(2,10))',
- 2 => 'in_array(fmod($n,100),range(11,19))',
- ),
- 'mk' =>
- array (
- 0 => 'fmod($n,10)==1&&$n!=11',
- ),
- 'cy' =>
- array (
- 0 => '$n==0',
- 1 => '$n==1',
- 2 => '$n==2',
- 3 => '$n==3',
- 4 => '$n==6',
- ),
- 'lag' =>
- array (
- 0 => '$n==0',
- 1 => '($n>=0&&$n<=2)&&$n!=0&&$n!=2',
- ),
- 'shi' =>
- array (
- 0 => '($n>=0&&$n<=1)',
- 1 => 'in_array($n,range(2,10))',
- ),
- 'br' =>
- array (
- 0 => 'fmod($n,10)==1&&!in_array(fmod($n,100),array(11,71,91))',
- 1 => 'fmod($n,10)==2&&!in_array(fmod($n,100),array(12,72,92))',
- 2 => 'in_array(fmod($n,10),array(3,4,9))&&!in_array(fmod($n,100),array_merge(range(10,19),range(70,79),range(90,99)))',
- 3 => 'fmod($n,1000000)==0&&$n!=0',
- ),
- 'ksh' =>
- array (
- 0 => '$n==0',
- 1 => '$n==1',
- ),
- 'tzm' =>
- array (
- 0 => '($n==0||$n==1)||in_array($n,range(11,99))',
- ),
- 'gv' =>
- array (
- 0 => 'in_array(fmod($n,10),array(1,2))||fmod($n,20)==0',
- ),
-);
diff --git a/framework/yii/i18n/data/plurals.xml b/framework/yii/i18n/data/plurals.xml
deleted file mode 100644
index 9227dc6..0000000
--- a/framework/yii/i18n/data/plurals.xml
+++ /dev/null
@@ -1,109 +0,0 @@
-
-
-
-
-
-
-
-
-
- n is 0
- n is 1
- n is 2
- n mod 100 in 3..10
- n mod 100 in 11..99
-
-
- n is 1
-
-
- n in 0..1
-
-
- n within 0..2 and n is not 2
-
-
- n is 0
- n mod 10 is 1 and n mod 100 is not 11
-
-
- n is 1
- n is 2
-
-
- n is 1
- n is 2
- n in 3..6
- n in 7..10
-
-
- n is 1
- n is 0 OR n is not 1 AND n mod 100 in 1..19
-
-
- n mod 10 is 1 and n mod 100 not in 11..19
- n mod 10 in 2..9 and n mod 100 not in 11..19
-
-
- n mod 10 is 1 and n mod 100 is not 11
- n mod 10 in 2..4 and n mod 100 not in 12..14
- n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14
-
-
-
- n is 1
- n in 2..4
-
-
- n is 1
- n mod 10 in 2..4 and n mod 100 not in 12..14
- n is not 1 and n mod 10 in 0..1 or n mod 10 in 5..9 or n mod 100 in 12..14
-
-
-
-
- n mod 100 is 1
- n mod 100 is 2
- n mod 100 in 3..4
-
-
- n is 1
- n is 0 or n mod 100 in 2..10
- n mod 100 in 11..19
-
-
- n mod 10 is 1 and n is not 11
-
-
- n is 0
- n is 1
- n is 2
- n is 3
- n is 6
-
-
- n is 0
- n within 0..2 and n is not 0 and n is not 2
-
-
- n within 0..1
- n in 2..10
-
-
- n mod 10 is 1 and n mod 100 not in 11,71,91
- n mod 10 is 2 and n mod 100 not in 12,72,92
- n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99
- n mod 1000000 is 0 and n is not 0
-
-
- n is 0
- n is 1
-
-
- n in 0..1 or n in 11..99
-
-
- n mod 10 in 1..2 or n mod 20 is 0
-
-
-
diff --git a/framework/yii/jui/Accordion.php b/framework/yii/jui/Accordion.php
deleted file mode 100644
index 898649e..0000000
--- a/framework/yii/jui/Accordion.php
+++ /dev/null
@@ -1,133 +0,0 @@
- array(
- * array(
- * 'header' => 'Section 1',
- * 'content' => 'Mauris mauris ante, blandit et, ultrices a, suscipit eget...',
- * ),
- * array(
- * 'header' => 'Section 2',
- * 'headerOptions' => array(
- * 'tag' => 'h3',
- * ),
- * 'content' => 'Sed non urna. Phasellus eu ligula. Vestibulum sit amet purus...',
- * 'options' => array(
- * 'tag' => 'div',
- * ),
- * ),
- * ),
- * 'options' => array(
- * 'tag' => 'div',
- * ),
- * 'itemOptions' => array(
- * 'tag' => 'div',
- * ),
- * 'headerOptions' => array(
- * 'tag' => 'h3',
- * ),
- * 'clientOptions' => array(
- * 'collapsible' => false,
- * ),
- * ));
- * ```
- *
- * @see http://api.jqueryui.com/accordion/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class Accordion extends Widget
-{
- /**
- * @var array the HTML attributes for the widget container tag. The following special options are recognized:
- *
- * - tag: string, defaults to "div", the tag name of the container tag of this widget
- */
- public $options = array();
- /**
- * @var array list of collapsible items. Each item can be an array of the following structure:
- *
- * ~~~
- * array(
- * 'header' => 'Item header',
- * 'content' => 'Item content',
- * // the HTML attributes of the item header container tag. This will overwrite "headerOptions".
- * 'headerOptions' => array(),
- * // the HTML attributes of the item container tag. This will overwrite "itemOptions".
- * 'options' => array(),
- * )
- * ~~~
- */
- public $items = array();
- /**
- * @var array list of HTML attributes for the item container tags. This will be overwritten
- * by the "options" set in individual [[items]]. The following special options are recognized:
- *
- * - tag: string, defaults to "div", the tag name of the item container tags.
- */
- public $itemOptions = array();
- /**
- * @var array list of HTML attributes for the item header container tags. This will be overwritten
- * by the "headerOptions" set in individual [[items]]. The following special options are recognized:
- *
- * - tag: string, defaults to "h3", the tag name of the item container tags.
- */
- public $headerOptions = array();
-
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- $options = $this->options;
- $tag = ArrayHelper::remove($options, 'tag', 'div');
- echo Html::beginTag($tag, $options) . "\n";
- echo $this->renderItems() . "\n";
- echo Html::endTag($tag) . "\n";
- $this->registerWidget('accordion');
- }
-
- /**
- * Renders collapsible items as specified on [[items]].
- * @return string the rendering result.
- * @throws InvalidConfigException.
- */
- protected function renderItems()
- {
- $items = array();
- foreach ($this->items as $item) {
- if (!isset($item['header'])) {
- throw new InvalidConfigException("The 'header' option is required.");
- }
- if (!isset($item['content'])) {
- throw new InvalidConfigException("The 'content' option is required.");
- }
- $headerOptions = array_merge($this->headerOptions, ArrayHelper::getValue($item, 'headerOptions', array()));
- $headerTag = ArrayHelper::remove($headerOptions, 'tag', 'h3');
- $items[] = Html::tag($headerTag, $item['header'], $headerOptions);
- $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', array()));
- $tag = ArrayHelper::remove($options, 'tag', 'div');
- $items[] = Html::tag($tag, $item['content'], $options);
- }
-
- return implode("\n", $items);
- }
-}
diff --git a/framework/yii/jui/AutoComplete.php b/framework/yii/jui/AutoComplete.php
deleted file mode 100644
index 44ca23d..0000000
--- a/framework/yii/jui/AutoComplete.php
+++ /dev/null
@@ -1,66 +0,0 @@
- $model,
- * 'attribute' => 'country',
- * 'clientOptions' => array(
- * 'source' => array('USA', 'RUS'),
- * ),
- * ));
- * ```
- *
- * The following example will use the name property instead:
- *
- * ```php
- * echo AutoComplete::widget(array(
- * 'name' => 'country',
- * 'clientOptions' => array(
- * 'source' => array('USA', 'RUS'),
- * ),
- * ));
- *```
- *
- * @see http://api.jqueryui.com/autocomplete/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class AutoComplete extends InputWidget
-{
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo $this->renderWidget();
- $this->registerWidget('autocomplete');
- }
-
- /**
- * Renders the AutoComplete widget.
- * @return string the rendering result.
- */
- public function renderWidget()
- {
- if ($this->hasModel()) {
- return Html::activeTextInput($this->model, $this->attribute, $this->options);
- } else {
- return Html::textInput($this->name, $this->value, $this->options);
- }
- }
-}
diff --git a/framework/yii/jui/DatePicker.php b/framework/yii/jui/DatePicker.php
deleted file mode 100644
index 1138b73..0000000
--- a/framework/yii/jui/DatePicker.php
+++ /dev/null
@@ -1,99 +0,0 @@
- 'ru',
- * 'model' => $model,
- * 'attribute' => 'country',
- * 'clientOptions' => array(
- * 'dateFormat' => 'yy-mm-dd',
- * ),
- * ));
- * ```
- *
- * The following example will use the name property instead:
- *
- * ```php
- * echo DatePicker::widget(array(
- * 'language' => 'ru',
- * 'name' => 'country',
- * 'clientOptions' => array(
- * 'dateFormat' => 'yy-mm-dd',
- * ),
- * ));
- *```
- *
- * @see http://api.jqueryui.com/datepicker/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class DatePicker extends InputWidget
-{
- /**
- * @var string the locale ID (eg 'fr', 'de') for the language to be used by the date picker.
- * If this property set to false, I18N will not be involved. That is, the date picker will show in English.
- */
- public $language = false;
- /**
- * @var boolean If true, shows the widget as an inline calendar and the input as a hidden field.
- */
- public $inline = false;
-
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo $this->renderWidget() . "\n";
- $this->registerWidget('datepicker');
- if ($this->language !== false) {
- $this->getView()->registerAssetBundle("yii/jui/datepicker/i18n/$this->language");
- }
- }
-
- /**
- * Renders the DatePicker widget.
- * @return string the rendering result.
- */
- protected function renderWidget()
- {
- $contents = array();
-
- if ($this->inline === false) {
- if ($this->hasModel()) {
- $contents[] = Html::activeTextInput($this->model, $this->attribute, $this->options);
- } else {
- $contents[] = Html::textInput($this->name, $this->value, $this->options);
- }
- } else {
- if ($this->hasModel()) {
- $contents[] = Html::activeHiddenInput($this->model, $this->attribute, $this->options);
- $this->clientOptions['defaultDate'] = $this->model->{$this->attribute};
- } else {
- $contents[] = Html::hiddenInput($this->name, $this->value, $this->options);
- $this->clientOptions['defaultDate'] = $this->value;
- }
- $this->clientOptions['altField'] = '#' . $this->options['id'];
- $this->options['id'] .= '-container';
- $contents[] = Html::tag('div', null, $this->options);
- }
-
- return implode("\n", $contents);
- }
-}
diff --git a/framework/yii/jui/Dialog.php b/framework/yii/jui/Dialog.php
deleted file mode 100644
index f4b3b12..0000000
--- a/framework/yii/jui/Dialog.php
+++ /dev/null
@@ -1,52 +0,0 @@
- array(
- * 'modal' => true,
- * ),
- * ));
- *
- * echo 'Dialog contents here...';
- *
- * Dialog::end();
- * ```
- *
- * @see http://api.jqueryui.com/dialog/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class Dialog extends Widget
-{
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
- echo Html::beginTag('div', $this->options) . "\n";
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::endTag('div') . "\n";
- $this->registerWidget('dialog');
- }
-}
diff --git a/framework/yii/jui/Draggable.php b/framework/yii/jui/Draggable.php
deleted file mode 100644
index c73f269..0000000
--- a/framework/yii/jui/Draggable.php
+++ /dev/null
@@ -1,52 +0,0 @@
- array(
- * 'grid' => array(50, 20),
- * ),
- * ));
- *
- * echo 'Draggable contents here...';
- *
- * Draggable::end();
- * ```
- *
- * @see http://api.jqueryui.com/draggable/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class Draggable extends Widget
-{
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
- echo Html::beginTag('div', $this->options) . "\n";
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::endTag('div') . "\n";
- $this->registerWidget('draggable', false);
- }
-}
diff --git a/framework/yii/jui/Droppable.php b/framework/yii/jui/Droppable.php
deleted file mode 100644
index 2f580bd..0000000
--- a/framework/yii/jui/Droppable.php
+++ /dev/null
@@ -1,52 +0,0 @@
- array(
- * 'accept' => '.special',
- * ),
- * ));
- *
- * echo 'Droppable body here...';
- *
- * Droppable::end();
- * ```
- *
- * @see http://api.jqueryui.com/droppable/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class Droppable extends Widget
-{
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
- echo Html::beginTag('div', $this->options) . "\n";
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::endTag('div') . "\n";
- $this->registerWidget('droppable', false);
- }
-}
diff --git a/framework/yii/jui/InputWidget.php b/framework/yii/jui/InputWidget.php
deleted file mode 100644
index e100d6c..0000000
--- a/framework/yii/jui/InputWidget.php
+++ /dev/null
@@ -1,59 +0,0 @@
-
- * @since 2.0
- */
-class InputWidget extends Widget
-{
- /**
- * @var Model the data model that this widget is associated with.
- */
- public $model;
- /**
- * @var string the model attribute that this widget is associated with.
- */
- public $attribute;
- /**
- * @var string the input name. This must be set if [[model]] and [[attribute]] are not set.
- */
- public $name;
- /**
- * @var string the input value.
- */
- public $value;
-
-
- /**
- * Initializes the widget.
- * If you override this method, make sure you call the parent implementation first.
- */
- public function init()
- {
- if (!$this->hasModel() && $this->name === null) {
- throw new InvalidConfigException("Either 'name' or 'model' and 'attribute' properties must be specified.");
- }
- parent::init();
- }
-
- /**
- * @return boolean whether this widget is associated with a data model.
- */
- protected function hasModel()
- {
- return $this->model instanceof Model && $this->attribute !== null;
- }
-}
diff --git a/framework/yii/jui/Menu.php b/framework/yii/jui/Menu.php
deleted file mode 100644
index 83523e7..0000000
--- a/framework/yii/jui/Menu.php
+++ /dev/null
@@ -1,84 +0,0 @@
-
- * @since 2.0
- */
-class Menu extends \yii\widgets\Menu
-{
- /**
- * @var array the options for the underlying jQuery UI widget.
- * Please refer to the corresponding jQuery UI widget Web page for possible options.
- * For example, [this page](http://api.jqueryui.com/accordion/) shows
- * how to use the "Accordion" widget and the supported options (e.g. "header").
- */
- public $clientOptions = array();
- /**
- * @var array the event handlers for the underlying jQuery UI widget.
- * Please refer to the corresponding jQuery UI widget Web page for possible events.
- * For example, [this page](http://api.jqueryui.com/accordion/) shows
- * how to use the "Accordion" widget and the supported events (e.g. "create").
- */
- public $clientEvents = array();
-
-
- /**
- * Initializes the widget.
- * If you override this method, make sure you call the parent implementation first.
- */
- public function init()
- {
- parent::init();
- if (!isset($this->options['id'])) {
- $this->options['id'] = $this->getId();
- }
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- parent::run();
- $this->registerWidget('menu');
- }
-
- /**
- * Registers a specific jQuery UI widget and the related events
- * @param string $name the name of the jQuery UI widget
- */
- protected function registerWidget($name)
- {
- $id = $this->options['id'];
- $view = $this->getView();
- $view->registerAssetBundle("yii/jui/$name");
- $view->registerAssetBundle(Widget::$theme . "/$name");
-
- if ($this->clientOptions !== false) {
- $options = empty($this->clientOptions) ? '' : Json::encode($this->clientOptions);
- $js = "jQuery('#$id').$name($options);";
- $view->registerJs($js);
- }
-
- if (!empty($this->clientEvents)) {
- $js = array();
- foreach ($this->clientEvents as $event => $handler) {
- $js[] = "jQuery('#$id').on('$name$event', $handler);";
- }
- $view->registerJs(implode("\n", $js));
- }
- }
-}
diff --git a/framework/yii/jui/ProgressBar.php b/framework/yii/jui/ProgressBar.php
deleted file mode 100644
index a7697e5..0000000
--- a/framework/yii/jui/ProgressBar.php
+++ /dev/null
@@ -1,62 +0,0 @@
- array(
- * 'value' => 75,
- * ),
- * ));
- * ```
- *
- * The following example will show the content enclosed between the [[begin()]]
- * and [[end()]] calls within the widget container:
- *
- * ~~~php
- * ProgressBar::widget(array(
- * 'clientOptions' => array(
- * 'value' => 75,
- * ),
- * ));
- *
- * echo 'Loading...
';
- *
- * ProgressBar::end();
- * ~~~
- * @see http://api.jqueryui.com/progressbar/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class ProgressBar extends Widget
-{
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
- echo Html::beginTag('div', $this->options) . "\n";
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::endTag('div') . "\n";
- $this->registerWidget('progressbar');
- }
-}
diff --git a/framework/yii/jui/Resizable.php b/framework/yii/jui/Resizable.php
deleted file mode 100644
index ffc3501..0000000
--- a/framework/yii/jui/Resizable.php
+++ /dev/null
@@ -1,52 +0,0 @@
- array(
- * 'grid' => array(20, 10),
- * ),
- * ));
- *
- * echo 'Resizable contents here...';
- *
- * Resizable::end();
- * ```
- *
- * @see http://api.jqueryui.com/resizable/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class Resizable extends Widget
-{
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
- echo Html::beginTag('div', $this->options) . "\n";
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo Html::endTag('div') . "\n";
- $this->registerWidget('resizable');
- }
-}
diff --git a/framework/yii/jui/Selectable.php b/framework/yii/jui/Selectable.php
deleted file mode 100644
index a1a9b5d..0000000
--- a/framework/yii/jui/Selectable.php
+++ /dev/null
@@ -1,116 +0,0 @@
- array(
- * 'Item 1',
- * array(
- * 'content' => 'Item2',
- * ),
- * array(
- * 'content' => 'Item3',
- * 'options' => array(
- * 'tag' => 'li',
- * ),
- * ),
- * ),
- * 'options' => array(
- * 'tag' => 'ul',
- * ),
- * 'itemOptions' => array(
- * 'tag' => 'li',
- * ),
- * 'clientOptions' => array(
- * 'tolerance' => 'fit',
- * ),
- * ));
- * ```
- *
- * @see http://api.jqueryui.com/selectable/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class Selectable extends Widget
-{
- /**
- * @var array the HTML attributes for the widget container tag. The following special options are recognized:
- *
- * - tag: string, defaults to "ul", the tag name of the container tag of this widget
- */
- public $options = array();
- /**
- * @var array list of selectable items. Each item can be a string representing the item content
- * or an array of the following structure:
- *
- * ~~~
- * array(
- * 'content' => 'item content',
- * // the HTML attributes of the item container tag. This will overwrite "itemOptions".
- * 'options' => array(),
- * )
- * ~~~
- */
- public $items = array();
- /**
- * @var array list of HTML attributes for the item container tags. This will be overwritten
- * by the "options" set in individual [[items]]. The following special options are recognized:
- *
- * - tag: string, defaults to "li", the tag name of the item container tags.
- */
- public $itemOptions = array();
-
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- $options = $this->options;
- $tag = ArrayHelper::remove($options, 'tag', 'ul');
- echo Html::beginTag($tag, $options) . "\n";
- echo $this->renderItems() . "\n";
- echo Html::endTag($tag) . "\n";
- $this->registerWidget('selectable');
- }
-
- /**
- * Renders selectable items as specified on [[items]].
- * @return string the rendering result.
- * @throws InvalidConfigException.
- */
- public function renderItems()
- {
- $items = array();
- foreach ($this->items as $item) {
- $options = $this->itemOptions;
- $tag = ArrayHelper::remove($options, 'tag', 'li');
- if (is_array($item)) {
- if (!isset($item['content'])) {
- throw new InvalidConfigException("The 'content' option is required.");
- }
- $options = array_merge($options, ArrayHelper::getValue($item, 'options', array()));
- $tag = ArrayHelper::remove($options, 'tag', $tag);
- $items[] = Html::tag($tag, $item['content'], $options);
- } else {
- $items[] = Html::tag($tag, $item, $options);
- }
- }
- return implode("\n", $items);
- }
-}
diff --git a/framework/yii/jui/Sortable.php b/framework/yii/jui/Sortable.php
deleted file mode 100644
index 8524b5b..0000000
--- a/framework/yii/jui/Sortable.php
+++ /dev/null
@@ -1,116 +0,0 @@
- array(
- * 'Item 1',
- * array(
- * 'content' => 'Item2',
- * ),
- * array(
- * 'content' => 'Item3',
- * 'options' => array(
- * 'tag' => 'li',
- * ),
- * ),
- * ),
- * 'options' => array(
- * 'tag' => 'ul',
- * ),
- * 'itemOptions' => array(
- * 'tag' => 'li',
- * ),
- * 'clientOptions' => array(
- * 'cursor' => 'move',
- * ),
- * ));
- * ```
- *
- * @see http://api.jqueryui.com/sortable/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class Sortable extends Widget
-{
- /**
- * @var array the HTML attributes for the widget container tag. The following special options are recognized:
- *
- * - tag: string, defaults to "ul", the tag name of the container tag of this widget
- */
- public $options = array();
- /**
- * @var array list of sortable items. Each item can be a string representing the item content
- * or an array of the following structure:
- *
- * ~~~
- * array(
- * 'content' => 'item content',
- * // the HTML attributes of the item container tag. This will overwrite "itemOptions".
- * 'options' => array(),
- * )
- * ~~~
- */
- public $items = array();
- /**
- * @var array list of HTML attributes for the item container tags. This will be overwritten
- * by the "options" set in individual [[items]]. The following special options are recognized:
- *
- * - tag: string, defaults to "li", the tag name of the item container tags.
- */
- public $itemOptions = array();
-
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- $options = $this->options;
- $tag = ArrayHelper::remove($options, 'tag', 'ul');
- echo Html::beginTag($tag, $options) . "\n";
- echo $this->renderItems() . "\n";
- echo Html::endTag($tag) . "\n";
- $this->registerWidget('sortable', false);
- }
-
- /**
- * Renders sortable items as specified on [[items]].
- * @return string the rendering result.
- * @throws InvalidConfigException.
- */
- public function renderItems()
- {
- $items = array();
- foreach ($this->items as $item) {
- $options = $this->itemOptions;
- $tag = ArrayHelper::remove($options, 'tag', 'li');
- if (is_array($item)) {
- if (!isset($item['content'])) {
- throw new InvalidConfigException("The 'content' option is required.");
- }
- $options = array_merge($options, ArrayHelper::getValue($item, 'options', array()));
- $tag = ArrayHelper::remove($options, 'tag', $tag);
- $items[] = Html::tag($tag, $item['content'], $options);
- } else {
- $items[] = Html::tag($tag, $item, $options);
- }
- }
- return implode("\n", $items);
- }
-}
diff --git a/framework/yii/jui/Spinner.php b/framework/yii/jui/Spinner.php
deleted file mode 100644
index 8d80f89..0000000
--- a/framework/yii/jui/Spinner.php
+++ /dev/null
@@ -1,66 +0,0 @@
- $model,
- * 'attribute' => 'country',
- * 'clientOptions' => array(
- * 'step' => 2,
- * ),
- * ));
- * ```
- *
- * The following example will use the name property instead:
- *
- * ```php
- * echo Spinner::widget(array(
- * 'name' => 'country',
- * 'clientOptions' => array(
- * 'step' => 2,
- * ),
- * ));
- *```
- *
- * @see http://api.jqueryui.com/spinner/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class Spinner extends InputWidget
-{
- /**
- * Renders the widget.
- */
- public function run()
- {
- echo $this->renderWidget();
- $this->registerWidget('spinner');
- }
-
- /**
- * Renders the Spinner widget.
- * @return string the rendering result.
- */
- public function renderWidget()
- {
- if ($this->hasModel()) {
- return Html::activeTextInput($this->model, $this->attribute, $this->options);
- } else {
- return Html::textInput($this->name, $this->value, $this->options);
- }
- }
-}
diff --git a/framework/yii/jui/Tabs.php b/framework/yii/jui/Tabs.php
deleted file mode 100644
index 13bd4eb..0000000
--- a/framework/yii/jui/Tabs.php
+++ /dev/null
@@ -1,159 +0,0 @@
- array(
- * array(
- * 'label' => 'Tab one',
- * 'content' => 'Mauris mauris ante, blandit et, ultrices a, suscipit eget...',
- * ),
- * array(
- * 'label' => 'Tab two',
- * 'content' => 'Sed non urna. Phasellus eu ligula. Vestibulum sit amet purus...',
- * 'options' => array(
- * 'tag' => 'div',
- * ),
- * 'headerOptions' => array(
- * 'class' => 'my-class',
- * ),
- * ),
- * array(
- * 'label' => 'Tab with custom id',
- * 'content' => 'Morbi tincidunt, dui sit amet facilisis feugiat...',
- * 'options' => array(
- * 'id' => 'my-tab',
- * ),
- * ),
- * array(
- * 'label' => 'Ajax tab',
- * 'url' => array('ajax/content'),
- * ),
- * ),
- * 'options' => array(
- * 'tag' => 'div',
- * ),
- * 'itemOptions' => array(
- * 'tag' => 'div',
- * ),
- * 'headerOptions' => array(
- * 'class' => 'my-class',
- * ),
- * 'clientOptions' => array(
- * 'collapsible' => false,
- * ),
- * ));
- * ```
- *
- * @see http://api.jqueryui.com/tabs/
- * @author Alexander Kochetov
- * @since 2.0
- */
-class Tabs extends Widget
-{
- /**
- * @var array the HTML attributes for the widget container tag. The following special options are recognized:
- *
- * - tag: string, defaults to "div", the tag name of the container tag of this widget
- */
- public $options = array();
- /**
- * @var array list of tab items. Each item can be an array of the following structure:
- *
- * - label: string, required, specifies the header link label. When [[encodeLabels]] is true, the label
- * will be HTML-encoded.
- * - content: string, the content to show when corresponding tab is clicked. Can be omitted if url is specified.
- * - url: mixed, mixed, optional, the url to load tab contents via AJAX. It is required if no content is specified.
- * - template: string, optional, the header link template to render the header link. If none specified
- * [[linkTemplate]] will be used instead.
- * - options: array, optional, the HTML attributes of the header.
- * - headerOptions: array, optional, the HTML attributes for the header container tag.
- */
- public $items = array();
- /**
- * @var array list of HTML attributes for the item container tags. This will be overwritten
- * by the "options" set in individual [[items]]. The following special options are recognized:
- *
- * - tag: string, defaults to "div", the tag name of the item container tags.
- */
- public $itemOptions = array();
- /**
- * @var array list of HTML attributes for the header container tags. This will be overwritten
- * by the "headerOptions" set in individual [[items]].
- */
- public $headerOptions = array();
- /**
- * @var string the default header template to render the link.
- */
- public $linkTemplate = '{label} ';
- /**
- * @var boolean whether the labels for header items should be HTML-encoded.
- */
- public $encodeLabels = true;
-
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- $options = $this->options;
- $tag = ArrayHelper::remove($options, 'tag', 'div');
- echo Html::beginTag($tag, $options) . "\n";
- echo $this->renderItems() . "\n";
- echo Html::endTag($tag) . "\n";
- $this->registerWidget('tabs');
- }
-
- /**
- * Renders tab items as specified on [[items]].
- * @return string the rendering result.
- * @throws InvalidConfigException.
- */
- protected function renderItems()
- {
- $headers = array();
- $items = array();
- foreach ($this->items as $n => $item) {
- if (!isset($item['label'])) {
- throw new InvalidConfigException("The 'label' option is required.");
- }
- if (isset($item['url'])) {
- $url = Html::url($item['url']);
- } else {
- if (!isset($item['content'])) {
- throw new InvalidConfigException("The 'content' or 'url' option is required.");
- }
- $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', array()));
- $tag = ArrayHelper::remove($options, 'tag', 'div');
- if (!isset($options['id'])) {
- $options['id'] = $this->options['id'] . '-tab' . $n;
- }
- $url = '#' . $options['id'];
- $items[] = Html::tag($tag, $item['content'], $options);
- }
- $headerOptions = array_merge($this->headerOptions, ArrayHelper::getValue($item, 'headerOptions', array()));
- $template = ArrayHelper::getValue($item, 'template', $this->linkTemplate);
- $headers[] = Html::tag('li', strtr($template, array(
- '{label}' => $this->encodeLabels ? Html::encode($item['label']) : $item['label'],
- '{url}' => $url,
- )), $headerOptions);
- }
- return Html::tag('ul', implode("\n", $headers)) . "\n" . implode("\n", $items);
- }
-}
diff --git a/framework/yii/jui/Widget.php b/framework/yii/jui/Widget.php
deleted file mode 100644
index 5724919..0000000
--- a/framework/yii/jui/Widget.php
+++ /dev/null
@@ -1,85 +0,0 @@
-
- * @since 2.0
- */
-class Widget extends \yii\base\Widget
-{
- /**
- * @var string the jQuery UI theme bundle.
- */
- public static $theme = 'yii/jui/theme/base';
- /**
- * @var array the HTML attributes for the widget container tag.
- */
- public $options = array();
- /**
- * @var array the options for the underlying jQuery UI widget.
- * Please refer to the corresponding jQuery UI widget Web page for possible options.
- * For example, [this page](http://api.jqueryui.com/accordion/) shows
- * how to use the "Accordion" widget and the supported options (e.g. "header").
- */
- public $clientOptions = array();
- /**
- * @var array the event handlers for the underlying jQuery UI widget.
- * Please refer to the corresponding jQuery UI widget Web page for possible events.
- * For example, [this page](http://api.jqueryui.com/accordion/) shows
- * how to use the "Accordion" widget and the supported events (e.g. "create").
- */
- public $clientEvents = array();
-
-
- /**
- * Initializes the widget.
- * If you override this method, make sure you call the parent implementation first.
- */
- public function init()
- {
- parent::init();
- if (!isset($this->options['id'])) {
- $this->options['id'] = $this->getId();
- }
- }
-
- /**
- * Registers a specific jQuery UI widget and the related events
- * @param string $name the name of the jQuery UI widget
- * @param boolean $registerTheme whether register theme bundle
- */
- protected function registerWidget($name, $registerTheme = true)
- {
- $id = $this->options['id'];
- $view = $this->getView();
- $view->registerAssetBundle("yii/jui/$name");
- if ($registerTheme) {
- $view->registerAssetBundle(static::$theme . "/$name");
- }
-
- if ($this->clientOptions !== false) {
- $options = empty($this->clientOptions) ? '' : Json::encode($this->clientOptions);
- $js = "jQuery('#$id').$name($options);";
- $view->registerJs($js);
- }
-
- if (!empty($this->clientEvents)) {
- $js = array();
- foreach ($this->clientEvents as $event => $handler) {
- $js[] = "jQuery('#$id').on('$name$event', $handler);";
- }
- $view->registerJs(implode("\n", $js));
- }
- }
-}
diff --git a/framework/yii/jui/assets.php b/framework/yii/jui/assets.php
deleted file mode 100644
index d2d8f7c..0000000
--- a/framework/yii/jui/assets.php
+++ /dev/null
@@ -1,864 +0,0 @@
- array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.core.js',
- ),
- 'depends' => array('yii/jquery'),
- ),
- 'yii/jui/widget' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.widget.js',
- ),
- 'depends' => array('yii/jquery'),
- ),
- 'yii/jui/accordion' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.accordion.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/effect/all'),
- ),
- 'yii/jui/autocomplete' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.autocomplete.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/position', 'yii/jui/menu'),
- ),
- 'yii/jui/button' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.button.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget'),
- ),
- 'yii/jui/datepicker' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.datepicker.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/effect/all'),
- ),
- 'yii/jui/datepicker/i18n/af' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-af.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ar' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ar.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ar_DZ' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ar-DZ.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/az' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-az.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/be' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-be.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/bg' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-bg.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/bs' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-bs.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ca' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ca.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/cs' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-cs.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/cy_GB' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-cy-GB.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/da' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-da.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/de' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-de.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/el' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-el.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/en_AU' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-en-AU.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/en_GB' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-en-GB.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/en_NZ' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-en-NZ.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/eo' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-eo.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/es' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-es.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/et' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-et.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/eu' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-eu.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/fa' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-fa.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/fi' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-fi.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/fo' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-fo.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/fr' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-fr.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/fr_CA' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-fr-CA.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/fr_CH' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-fr-CH.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/gl' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-gl.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/he' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-he.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/hi' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-hi.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/hr' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-hr.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/hu' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-hu.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/hy' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-hy.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/id' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-id.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/is' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-is.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/it' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-it.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ja' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ja.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ka' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ka.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/kk' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-kk.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/km' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-km.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ko' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ko.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ky' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ky.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/lb' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-lb.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/lt' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-lt.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/lv' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-lv.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/mk' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-mk.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ml' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ml.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ms.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/nb' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-nb.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/nl' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-nl.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/nl_BE' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-nl-BE.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/nn' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-nn.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/no' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-no.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/pl' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-pl.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/pt' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-pt.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/pt_BR' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-pt-BR.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/rm' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-rm.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ro' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ro.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ru' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ru.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/sk' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-sk.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/sl' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-sl.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/sq' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-sq.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/sr' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-sr.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/sr_SR' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-sr-SR.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/sv' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-sv.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/ta' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-ta.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/th' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-th.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/tj' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-tj.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/tr' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-tr.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/uk' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-uk.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/vi' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-vi.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/zh_CN' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-zh-CN.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/zh_HK' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-zh-HK.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/datepicker/i18n/zh_TW' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'i18n/jquery.ui.datepicker-zh-TW.js',
- ),
- 'depends' => array('yii/jui/datepicker'),
- ),
- 'yii/jui/dialog' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.dialog.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/button', 'yii/jui/draggable', 'yii/jui/mouse', 'yii/jui/position', 'yii/jui/resizable', 'yii/jui/effect/all'),
- ),
- 'yii/jui/draggable' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.draggable.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/mouse'),
- ),
- 'yii/jui/droppable' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.droppable.js',
- ),
- 'depends' => array('yii/jui/draggable'),
- ),
- 'yii/jui/effect' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect.js',
- ),
- 'depends' => array('yii/jquery'),
- ),
- 'yii/jui/effect/all' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect.js',
- ),
- 'depends' => array('yii/jui/effect/blind', 'yii/jui/effect/bounce', 'yii/jui/effect/clip', 'yii/jui/effect/drop', 'yii/jui/effect/explode', 'yii/jui/effect/fade', 'yii/jui/effect/fold', 'yii/jui/effect/highlight', 'yii/jui/effect/pulsate', 'yii/jui/effect/scale', 'yii/jui/effect/shake', 'yii/jui/effect/slide', 'yii/jui/effect/transfer'),
- ),
- 'yii/jui/effect/blind' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-blind.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/bounce' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-bounce.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/clip' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-clip.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/drop' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-drop.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/explode' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-explode.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/fade' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-fade.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/fold' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-fold.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/highlight' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-highlight.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/pulsate' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-pulsate.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/scale' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-scale.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/shake' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-shake.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/slide' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-slide.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/effect/transfer' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.effect-transfer.js',
- ),
- 'depends' => array('yii/jui/effect'),
- ),
- 'yii/jui/menu' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.menu.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/position'),
- ),
- 'yii/jui/mouse' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.mouse.js',
- ),
- 'depends' => array('yii/jui/widget'),
- ),
- 'yii/jui/position' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.position.js',
- ),
- 'depends' => array('yii/jquery'),
- ),
- 'yii/jui/progressbar' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.progressbar.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget'),
- ),
- 'yii/jui/resizable' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.resizable.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/mouse'),
- ),
- 'yii/jui/selectable' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.selectable.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/mouse'),
- ),
- 'yii/jui/slider' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.slider.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/mouse'),
- ),
- 'yii/jui/sortable' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.sortable.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/mouse'),
- ),
- 'yii/jui/spinner' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.spinner.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/button'),
- ),
- 'yii/jui/tabs' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.tabs.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/effect/all'),
- ),
- 'yii/jui/tooltip' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'js' => array(
- 'jquery.ui.tooltip.js',
- ),
- 'depends' => array('yii/jui/core', 'yii/jui/widget', 'yii/jui/position', 'yii/jui/effect/all'),
- ),
- 'yii/jui/theme/base' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.theme.css',
- ),
- ),
- 'yii/jui/theme/base/core' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.core.css',
- ),
- 'depends' => array('yii/jui/theme/base'),
- ),
- 'yii/jui/theme/base/accordion' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.accordion.css',
- ),
- 'depends' => array('yii/jui/theme/base/core'),
- ),
- 'yii/jui/theme/base/autocomplete' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.autocomplete.css',
- ),
- 'depends' => array('yii/jui/theme/base/core', 'yii/jui/theme/base/menu'),
- ),
- 'yii/jui/theme/base/button' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.button.css',
- ),
- 'depends' => array('yii/jui/theme/base/core'),
- ),
- 'yii/jui/theme/base/datepicker' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.datepicker.css',
- ),
- 'depends' => array('yii/jui/theme/base/core'),
- ),
- 'yii/jui/theme/base/dialog' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.dialog.css',
- ),
- 'depends' => array('yii/jui/theme/base/core', 'yii/jui/theme/base/button', 'yii/jui/theme/base/resizable'),
- ),
- 'yii/jui/theme/base/menu' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.menu.css',
- ),
- 'depends' => array('yii/jui/theme/base/core'),
- ),
- 'yii/jui/theme/base/progressbar' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.progressbar.css',
- ),
- 'depends' => array('yii/jui/theme/base/core'),
- ),
- 'yii/jui/theme/base/resizable' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.resizable.css',
- ),
- 'depends' => array('yii/jui/theme/base/core'),
- ),
- 'yii/jui/theme/base/selectable' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.selectable.css',
- ),
- 'depends' => array('yii/jui/theme/base/core'),
- ),
- 'yii/jui/theme/base/slider' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.slider.css',
- ),
- 'depends' => array('yii/jui/theme/base/core'),
- ),
- 'yii/jui/theme/base/spinner' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.spinner.css',
- ),
- 'depends' => array('yii/jui/theme/base/core', 'yii/jui/theme/base/button'),
- ),
- 'yii/jui/theme/base/tabs' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.tabs.css',
- ),
- 'depends' => array('yii/jui/theme/base/core'),
- ),
- 'yii/jui/theme/base/tooltip' => array(
- 'sourcePath' => __DIR__ . '/assets',
- 'css' => array(
- 'themes/base/jquery.ui.tooltip.css',
- ),
- 'depends' => array('yii/jui/theme/base/core'),
- ),
-);
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-af.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-af.js
deleted file mode 100644
index 0922ef7..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-af.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Afrikaans initialisation for the jQuery UI date picker plugin. */
-/* Written by Renier Pretorius. */
-jQuery(function($){
- $.datepicker.regional['af'] = {
- closeText: 'Selekteer',
- prevText: 'Vorige',
- nextText: 'Volgende',
- currentText: 'Vandag',
- monthNames: ['Januarie','Februarie','Maart','April','Mei','Junie',
- 'Julie','Augustus','September','Oktober','November','Desember'],
- monthNamesShort: ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
- dayNames: ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrydag', 'Saterdag'],
- dayNamesShort: ['Son', 'Maa', 'Din', 'Woe', 'Don', 'Vry', 'Sat'],
- dayNamesMin: ['So','Ma','Di','Wo','Do','Vr','Sa'],
- weekHeader: 'Wk',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['af']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ar-DZ.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ar-DZ.js
deleted file mode 100644
index 7b175af..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ar-DZ.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Algerian Arabic Translation for jQuery UI date picker plugin. (can be used for Tunisia)*/
-/* Mohamed Cherif BOUCHELAGHEM -- cherifbouchelaghem@yahoo.fr */
-
-jQuery(function($){
- $.datepicker.regional['ar-DZ'] = {
- closeText: 'إغلاق',
- prevText: '<السابق',
- nextText: 'التالي>',
- currentText: 'اليوم',
- monthNames: ['جانفي', 'فيفري', 'مارس', 'أفريل', 'ماي', 'جوان',
- 'جويلية', 'أوت', 'سبتمبر','أكتوبر', 'نوفمبر', 'ديسمبر'],
- monthNamesShort: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
- dayNames: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
- dayNamesShort: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
- dayNamesMin: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
- weekHeader: 'أسبوع',
- dateFormat: 'dd/mm/yy',
- firstDay: 6,
- isRTL: true,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['ar-DZ']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ar.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ar.js
deleted file mode 100644
index cef0f08..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ar.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Arabic Translation for jQuery UI date picker plugin. */
-/* Khaled Alhourani -- me@khaledalhourani.com */
-/* NOTE: monthNames are the original months names and they are the Arabic names, not the new months name فبراير - يناير and there isn't any Arabic roots for these months */
-jQuery(function($){
- $.datepicker.regional['ar'] = {
- closeText: 'إغلاق',
- prevText: '<السابق',
- nextText: 'التالي>',
- currentText: 'اليوم',
- monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'مايو', 'حزيران',
- 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
- monthNamesShort: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
- dayNames: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
- dayNamesShort: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
- dayNamesMin: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
- weekHeader: 'أسبوع',
- dateFormat: 'dd/mm/yy',
- firstDay: 6,
- isRTL: true,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['ar']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-az.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-az.js
deleted file mode 100644
index a133a9e..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-az.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Azerbaijani (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by Jamil Najafov (necefov33@gmail.com). */
-jQuery(function($) {
- $.datepicker.regional['az'] = {
- closeText: 'Bağla',
- prevText: '<Geri',
- nextText: 'İrəli>',
- currentText: 'Bugün',
- monthNames: ['Yanvar','Fevral','Mart','Aprel','May','İyun',
- 'İyul','Avqust','Sentyabr','Oktyabr','Noyabr','Dekabr'],
- monthNamesShort: ['Yan','Fev','Mar','Apr','May','İyun',
- 'İyul','Avq','Sen','Okt','Noy','Dek'],
- dayNames: ['Bazar','Bazar ertəsi','Çərşənbə axşamı','Çərşənbə','Cümə axşamı','Cümə','Şənbə'],
- dayNamesShort: ['B','Be','Ça','Ç','Ca','C','Ş'],
- dayNamesMin: ['B','B','Ç','С','Ç','C','Ş'],
- weekHeader: 'Hf',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['az']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-be.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-be.js
deleted file mode 100644
index 6ea12f7..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-be.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Belarusian initialisation for the jQuery UI date picker plugin. */
-/* Written by Pavel Selitskas */
-jQuery(function($){
- $.datepicker.regional['be'] = {
- closeText: 'Зачыніць',
- prevText: '←Папяр.',
- nextText: 'Наст.→',
- currentText: 'Сёньня',
- monthNames: ['Студзень','Люты','Сакавік','Красавік','Травень','Чэрвень',
- 'Ліпень','Жнівень','Верасень','Кастрычнік','Лістапад','Сьнежань'],
- monthNamesShort: ['Сту','Лют','Сак','Кра','Тра','Чэр',
- 'Ліп','Жні','Вер','Кас','Ліс','Сьн'],
- dayNames: ['нядзеля','панядзелак','аўторак','серада','чацьвер','пятніца','субота'],
- dayNamesShort: ['ндз','пнд','аўт','срд','чцв','птн','сбт'],
- dayNamesMin: ['Нд','Пн','Аў','Ср','Чц','Пт','Сб'],
- weekHeader: 'Тд',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['be']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-bg.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-bg.js
deleted file mode 100644
index 86ab885..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-bg.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Bulgarian initialisation for the jQuery UI date picker plugin. */
-/* Written by Stoyan Kyosev (http://svest.org). */
-jQuery(function($){
- $.datepicker.regional['bg'] = {
- closeText: 'затвори',
- prevText: '<назад',
- nextText: 'напред>',
- nextBigText: '>>',
- currentText: 'днес',
- monthNames: ['Януари','Февруари','Март','Април','Май','Юни',
- 'Юли','Август','Септември','Октомври','Ноември','Декември'],
- monthNamesShort: ['Яну','Фев','Мар','Апр','Май','Юни',
- 'Юли','Авг','Сеп','Окт','Нов','Дек'],
- dayNames: ['Неделя','Понеделник','Вторник','Сряда','Четвъртък','Петък','Събота'],
- dayNamesShort: ['Нед','Пон','Вто','Сря','Чет','Пет','Съб'],
- dayNamesMin: ['Не','По','Вт','Ср','Че','Пе','Съ'],
- weekHeader: 'Wk',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['bg']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-bs.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-bs.js
deleted file mode 100644
index f08870f..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-bs.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Bosnian i18n for the jQuery UI date picker plugin. */
-/* Written by Kenan Konjo. */
-jQuery(function($){
- $.datepicker.regional['bs'] = {
- closeText: 'Zatvori',
- prevText: '<',
- nextText: '>',
- currentText: 'Danas',
- monthNames: ['Januar','Februar','Mart','April','Maj','Juni',
- 'Juli','August','Septembar','Oktobar','Novembar','Decembar'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun',
- 'Jul','Aug','Sep','Okt','Nov','Dec'],
- dayNames: ['Nedelja','Ponedeljak','Utorak','Srijeda','Četvrtak','Petak','Subota'],
- dayNamesShort: ['Ned','Pon','Uto','Sri','Čet','Pet','Sub'],
- dayNamesMin: ['Ne','Po','Ut','Sr','Če','Pe','Su'],
- weekHeader: 'Wk',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['bs']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ca.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ca.js
deleted file mode 100644
index a10b549..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ca.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Inicialització en català per a l'extensió 'UI date picker' per jQuery. */
-/* Writers: (joan.leon@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['ca'] = {
- closeText: 'Tanca',
- prevText: 'Anterior',
- nextText: 'Següent',
- currentText: 'Avui',
- monthNames: ['gener','febrer','març','abril','maig','juny',
- 'juliol','agost','setembre','octubre','novembre','desembre'],
- monthNamesShort: ['gen','feb','març','abr','maig','juny',
- 'jul','ag','set','oct','nov','des'],
- dayNames: ['diumenge','dilluns','dimarts','dimecres','dijous','divendres','dissabte'],
- dayNamesShort: ['dg','dl','dt','dc','dj','dv','ds'],
- dayNamesMin: ['dg','dl','dt','dc','dj','dv','ds'],
- weekHeader: 'Set',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['ca']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-cs.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-cs.js
deleted file mode 100644
index b96b1a5..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-cs.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Czech initialisation for the jQuery UI date picker plugin. */
-/* Written by Tomas Muller (tomas@tomas-muller.net). */
-jQuery(function($){
- $.datepicker.regional['cs'] = {
- closeText: 'Zavřít',
- prevText: '<Dříve',
- nextText: 'Později>',
- currentText: 'Nyní',
- monthNames: ['leden','únor','březen','duben','květen','červen',
- 'červenec','srpen','září','říjen','listopad','prosinec'],
- monthNamesShort: ['led','úno','bře','dub','kvě','čer',
- 'čvc','srp','zář','říj','lis','pro'],
- dayNames: ['neděle', 'pondělí', 'úterý', 'středa', 'čtvrtek', 'pátek', 'sobota'],
- dayNamesShort: ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'],
- dayNamesMin: ['ne','po','út','st','čt','pá','so'],
- weekHeader: 'Týd',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['cs']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-cy-GB.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-cy-GB.js
deleted file mode 100644
index cf3a38e..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-cy-GB.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Welsh/UK initialisation for the jQuery UI date picker plugin. */
-/* Written by William Griffiths. */
-jQuery(function($){
- $.datepicker.regional['cy-GB'] = {
- closeText: 'Done',
- prevText: 'Prev',
- nextText: 'Next',
- currentText: 'Today',
- monthNames: ['Ionawr','Chwefror','Mawrth','Ebrill','Mai','Mehefin',
- 'Gorffennaf','Awst','Medi','Hydref','Tachwedd','Rhagfyr'],
- monthNamesShort: ['Ion', 'Chw', 'Maw', 'Ebr', 'Mai', 'Meh',
- 'Gor', 'Aws', 'Med', 'Hyd', 'Tac', 'Rha'],
- dayNames: ['Dydd Sul', 'Dydd Llun', 'Dydd Mawrth', 'Dydd Mercher', 'Dydd Iau', 'Dydd Gwener', 'Dydd Sadwrn'],
- dayNamesShort: ['Sul', 'Llu', 'Maw', 'Mer', 'Iau', 'Gwe', 'Sad'],
- dayNamesMin: ['Su','Ll','Ma','Me','Ia','Gw','Sa'],
- weekHeader: 'Wy',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['cy-GB']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-da.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-da.js
deleted file mode 100644
index 7e42948..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-da.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Danish initialisation for the jQuery UI date picker plugin. */
-/* Written by Jan Christensen ( deletestuff@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['da'] = {
- closeText: 'Luk',
- prevText: '<Forrige',
- nextText: 'Næste>',
- currentText: 'Idag',
- monthNames: ['Januar','Februar','Marts','April','Maj','Juni',
- 'Juli','August','September','Oktober','November','December'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun',
- 'Jul','Aug','Sep','Okt','Nov','Dec'],
- dayNames: ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'],
- dayNamesShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'],
- dayNamesMin: ['Sø','Ma','Ti','On','To','Fr','Lø'],
- weekHeader: 'Uge',
- dateFormat: 'dd-mm-yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['da']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-de.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-de.js
deleted file mode 100644
index abe75c4..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-de.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* German initialisation for the jQuery UI date picker plugin. */
-/* Written by Milian Wolff (mail@milianw.de). */
-jQuery(function($){
- $.datepicker.regional['de'] = {
- closeText: 'Schließen',
- prevText: '<Zurück',
- nextText: 'Vor>',
- currentText: 'Heute',
- monthNames: ['Januar','Februar','März','April','Mai','Juni',
- 'Juli','August','September','Oktober','November','Dezember'],
- monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun',
- 'Jul','Aug','Sep','Okt','Nov','Dez'],
- dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
- dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
- dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'],
- weekHeader: 'KW',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['de']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-el.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-el.js
deleted file mode 100644
index 1ac4756..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-el.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Greek (el) initialisation for the jQuery UI date picker plugin. */
-/* Written by Alex Cicovic (http://www.alexcicovic.com) */
-jQuery(function($){
- $.datepicker.regional['el'] = {
- closeText: 'Κλείσιμο',
- prevText: 'Προηγούμενος',
- nextText: 'Επόμενος',
- currentText: 'Τρέχων Μήνας',
- monthNames: ['Ιανουάριος','Φεβρουάριος','Μάρτιος','Απρίλιος','Μάιος','Ιούνιος',
- 'Ιούλιος','Αύγουστος','Σεπτέμβριος','Οκτώβριος','Νοέμβριος','Δεκέμβριος'],
- monthNamesShort: ['Ιαν','Φεβ','Μαρ','Απρ','Μαι','Ιουν',
- 'Ιουλ','Αυγ','Σεπ','Οκτ','Νοε','Δεκ'],
- dayNames: ['Κυριακή','Δευτέρα','Τρίτη','Τετάρτη','Πέμπτη','Παρασκευή','Σάββατο'],
- dayNamesShort: ['Κυρ','Δευ','Τρι','Τετ','Πεμ','Παρ','Σαβ'],
- dayNamesMin: ['Κυ','Δε','Τρ','Τε','Πε','Πα','Σα'],
- weekHeader: 'Εβδ',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['el']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-en-AU.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-en-AU.js
deleted file mode 100644
index c1a1020..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-en-AU.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* English/Australia initialisation for the jQuery UI date picker plugin. */
-/* Based on the en-GB initialisation. */
-jQuery(function($){
- $.datepicker.regional['en-AU'] = {
- closeText: 'Done',
- prevText: 'Prev',
- nextText: 'Next',
- currentText: 'Today',
- monthNames: ['January','February','March','April','May','June',
- 'July','August','September','October','November','December'],
- monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
- dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
- dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
- dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'],
- weekHeader: 'Wk',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['en-AU']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-en-GB.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-en-GB.js
deleted file mode 100644
index 16a096e..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-en-GB.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* English/UK initialisation for the jQuery UI date picker plugin. */
-/* Written by Stuart. */
-jQuery(function($){
- $.datepicker.regional['en-GB'] = {
- closeText: 'Done',
- prevText: 'Prev',
- nextText: 'Next',
- currentText: 'Today',
- monthNames: ['January','February','March','April','May','June',
- 'July','August','September','October','November','December'],
- monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
- dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
- dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
- dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'],
- weekHeader: 'Wk',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['en-GB']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-en-NZ.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-en-NZ.js
deleted file mode 100644
index 7819df0..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-en-NZ.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* English/New Zealand initialisation for the jQuery UI date picker plugin. */
-/* Based on the en-GB initialisation. */
-jQuery(function($){
- $.datepicker.regional['en-NZ'] = {
- closeText: 'Done',
- prevText: 'Prev',
- nextText: 'Next',
- currentText: 'Today',
- monthNames: ['January','February','March','April','May','June',
- 'July','August','September','October','November','December'],
- monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
- dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
- dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
- dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'],
- weekHeader: 'Wk',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['en-NZ']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-eo.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-eo.js
deleted file mode 100644
index 39e44fc..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-eo.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Esperanto initialisation for the jQuery UI date picker plugin. */
-/* Written by Olivier M. (olivierweb@ifrance.com). */
-jQuery(function($){
- $.datepicker.regional['eo'] = {
- closeText: 'Fermi',
- prevText: '<Anta',
- nextText: 'Sekv>',
- currentText: 'Nuna',
- monthNames: ['Januaro','Februaro','Marto','Aprilo','Majo','Junio',
- 'Julio','Aŭgusto','Septembro','Oktobro','Novembro','Decembro'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun',
- 'Jul','Aŭg','Sep','Okt','Nov','Dec'],
- dayNames: ['Dimanĉo','Lundo','Mardo','Merkredo','Ĵaŭdo','Vendredo','Sabato'],
- dayNamesShort: ['Dim','Lun','Mar','Mer','Ĵaŭ','Ven','Sab'],
- dayNamesMin: ['Di','Lu','Ma','Me','Ĵa','Ve','Sa'],
- weekHeader: 'Sb',
- dateFormat: 'dd/mm/yy',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['eo']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-es.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-es.js
deleted file mode 100644
index 97a2d6e..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-es.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Inicialización en español para la extensión 'UI date picker' para jQuery. */
-/* Traducido por Vester (xvester@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['es'] = {
- closeText: 'Cerrar',
- prevText: '<Ant',
- nextText: 'Sig>',
- currentText: 'Hoy',
- monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio',
- 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
- monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun',
- 'Jul','Ago','Sep','Oct','Nov','Dic'],
- dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'],
- dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
- dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
- weekHeader: 'Sm',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['es']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-et.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-et.js
deleted file mode 100644
index 62cbea8..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-et.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Estonian initialisation for the jQuery UI date picker plugin. */
-/* Written by Mart Sõmermaa (mrts.pydev at gmail com). */
-jQuery(function($){
- $.datepicker.regional['et'] = {
- closeText: 'Sulge',
- prevText: 'Eelnev',
- nextText: 'Järgnev',
- currentText: 'Täna',
- monthNames: ['Jaanuar','Veebruar','Märts','Aprill','Mai','Juuni',
- 'Juuli','August','September','Oktoober','November','Detsember'],
- monthNamesShort: ['Jaan', 'Veebr', 'Märts', 'Apr', 'Mai', 'Juuni',
- 'Juuli', 'Aug', 'Sept', 'Okt', 'Nov', 'Dets'],
- dayNames: ['Pühapäev', 'Esmaspäev', 'Teisipäev', 'Kolmapäev', 'Neljapäev', 'Reede', 'Laupäev'],
- dayNamesShort: ['Pühap', 'Esmasp', 'Teisip', 'Kolmap', 'Neljap', 'Reede', 'Laup'],
- dayNamesMin: ['P','E','T','K','N','R','L'],
- weekHeader: 'näd',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['et']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-eu.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-eu.js
deleted file mode 100644
index a71db2c..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-eu.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Euskarako oinarria 'UI date picker' jquery-ko extentsioarentzat */
-/* Karrikas-ek itzulia (karrikas@karrikas.com) */
-jQuery(function($){
- $.datepicker.regional['eu'] = {
- closeText: 'Egina',
- prevText: '<Aur',
- nextText: 'Hur>',
- currentText: 'Gaur',
- monthNames: ['urtarrila','otsaila','martxoa','apirila','maiatza','ekaina',
- 'uztaila','abuztua','iraila','urria','azaroa','abendua'],
- monthNamesShort: ['urt.','ots.','mar.','api.','mai.','eka.',
- 'uzt.','abu.','ira.','urr.','aza.','abe.'],
- dayNames: ['igandea','astelehena','asteartea','asteazkena','osteguna','ostirala','larunbata'],
- dayNamesShort: ['ig.','al.','ar.','az.','og.','ol.','lr.'],
- dayNamesMin: ['ig','al','ar','az','og','ol','lr'],
- weekHeader: 'As',
- dateFormat: 'yy-mm-dd',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['eu']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fa.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fa.js
deleted file mode 100644
index bb957f6..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fa.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/* Persian (Farsi) Translation for the jQuery UI date picker plugin. */
-/* Javad Mowlanezhad -- jmowla@gmail.com */
-/* Jalali calendar should supported soon! (Its implemented but I have to test it) */
-jQuery(function($) {
- $.datepicker.regional['fa'] = {
- closeText: 'بستن',
- prevText: '<قبلی',
- nextText: 'بعدی>',
- currentText: 'امروز',
- monthNames: [
- 'فروردين',
- 'ارديبهشت',
- 'خرداد',
- 'تير',
- 'مرداد',
- 'شهريور',
- 'مهر',
- 'آبان',
- 'آذر',
- 'دی',
- 'بهمن',
- 'اسفند'
- ],
- monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'],
- dayNames: [
- 'يکشنبه',
- 'دوشنبه',
- 'سهشنبه',
- 'چهارشنبه',
- 'پنجشنبه',
- 'جمعه',
- 'شنبه'
- ],
- dayNamesShort: [
- 'ی',
- 'د',
- 'س',
- 'چ',
- 'پ',
- 'ج',
- 'ش'
- ],
- dayNamesMin: [
- 'ی',
- 'د',
- 'س',
- 'چ',
- 'پ',
- 'ج',
- 'ش'
- ],
- weekHeader: 'هف',
- dateFormat: 'yy/mm/dd',
- firstDay: 6,
- isRTL: true,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['fa']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fi.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fi.js
deleted file mode 100644
index bd6d994..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fi.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Finnish initialisation for the jQuery UI date picker plugin. */
-/* Written by Harri Kilpiö (harrikilpio@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['fi'] = {
- closeText: 'Sulje',
- prevText: '«Edellinen',
- nextText: 'Seuraava»',
- currentText: 'Tänään',
- monthNames: ['Tammikuu','Helmikuu','Maaliskuu','Huhtikuu','Toukokuu','Kesäkuu',
- 'Heinäkuu','Elokuu','Syyskuu','Lokakuu','Marraskuu','Joulukuu'],
- monthNamesShort: ['Tammi','Helmi','Maalis','Huhti','Touko','Kesä',
- 'Heinä','Elo','Syys','Loka','Marras','Joulu'],
- dayNamesShort: ['Su','Ma','Ti','Ke','To','Pe','La'],
- dayNames: ['Sunnuntai','Maanantai','Tiistai','Keskiviikko','Torstai','Perjantai','Lauantai'],
- dayNamesMin: ['Su','Ma','Ti','Ke','To','Pe','La'],
- weekHeader: 'Vk',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['fi']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fo.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fo.js
deleted file mode 100644
index cb0e3de..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fo.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Faroese initialisation for the jQuery UI date picker plugin */
-/* Written by Sverri Mohr Olsen, sverrimo@gmail.com */
-jQuery(function($){
- $.datepicker.regional['fo'] = {
- closeText: 'Lat aftur',
- prevText: '<Fyrra',
- nextText: 'Næsta>',
- currentText: 'Í dag',
- monthNames: ['Januar','Februar','Mars','Apríl','Mei','Juni',
- 'Juli','August','September','Oktober','November','Desember'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun',
- 'Jul','Aug','Sep','Okt','Nov','Des'],
- dayNames: ['Sunnudagur','Mánadagur','Týsdagur','Mikudagur','Hósdagur','Fríggjadagur','Leyardagur'],
- dayNamesShort: ['Sun','Mán','Týs','Mik','Hós','Frí','Ley'],
- dayNamesMin: ['Su','Má','Tý','Mi','Hó','Fr','Le'],
- weekHeader: 'Vk',
- dateFormat: 'dd-mm-yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['fo']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fr-CA.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fr-CA.js
deleted file mode 100644
index e208221..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fr-CA.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Canadian-French initialisation for the jQuery UI date picker plugin. */
-jQuery(function ($) {
- $.datepicker.regional['fr-CA'] = {
- closeText: 'Fermer',
- prevText: 'Précédent',
- nextText: 'Suivant',
- currentText: 'Aujourd\'hui',
- monthNames: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
- 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
- monthNamesShort: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin',
- 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
- dayNames: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
- dayNamesShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
- dayNamesMin: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
- weekHeader: 'Sem.',
- dateFormat: 'yy-mm-dd',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''
- };
- $.datepicker.setDefaults($.datepicker.regional['fr-CA']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fr-CH.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fr-CH.js
deleted file mode 100644
index e574537..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fr-CH.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Swiss-French initialisation for the jQuery UI date picker plugin. */
-/* Written Martin Voelkle (martin.voelkle@e-tc.ch). */
-jQuery(function($){
- $.datepicker.regional['fr-CH'] = {
- closeText: 'Fermer',
- prevText: '<Préc',
- nextText: 'Suiv>',
- currentText: 'Courant',
- monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin',
- 'Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
- monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun',
- 'Jul','Aoû','Sep','Oct','Nov','Déc'],
- dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
- dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'],
- dayNamesMin: ['Di','Lu','Ma','Me','Je','Ve','Sa'],
- weekHeader: 'Sm',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['fr-CH']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fr.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fr.js
deleted file mode 100644
index 934afd1..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-fr.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/* French initialisation for the jQuery UI date picker plugin. */
-/* Written by Keith Wood (kbwood{at}iinet.com.au),
- Stéphane Nahmani (sholby@sholby.net),
- Stéphane Raimbault */
-jQuery(function($){
- $.datepicker.regional['fr'] = {
- closeText: 'Fermer',
- prevText: 'Précédent',
- nextText: 'Suivant',
- currentText: 'Aujourd\'hui',
- monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin',
- 'Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
- monthNamesShort: ['Janv.','Févr.','Mars','Avril','Mai','Juin',
- 'Juil.','Août','Sept.','Oct.','Nov.','Déc.'],
- dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
- dayNamesShort: ['Dim.','Lun.','Mar.','Mer.','Jeu.','Ven.','Sam.'],
- dayNamesMin: ['D','L','M','M','J','V','S'],
- weekHeader: 'Sem.',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['fr']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-gl.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-gl.js
deleted file mode 100644
index 59b989a..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-gl.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Galician localization for 'UI date picker' jQuery extension. */
-/* Translated by Jorge Barreiro . */
-jQuery(function($){
- $.datepicker.regional['gl'] = {
- closeText: 'Pechar',
- prevText: '<Ant',
- nextText: 'Seg>',
- currentText: 'Hoxe',
- monthNames: ['Xaneiro','Febreiro','Marzo','Abril','Maio','Xuño',
- 'Xullo','Agosto','Setembro','Outubro','Novembro','Decembro'],
- monthNamesShort: ['Xan','Feb','Mar','Abr','Mai','Xuñ',
- 'Xul','Ago','Set','Out','Nov','Dec'],
- dayNames: ['Domingo','Luns','Martes','Mércores','Xoves','Venres','Sábado'],
- dayNamesShort: ['Dom','Lun','Mar','Mér','Xov','Ven','Sáb'],
- dayNamesMin: ['Do','Lu','Ma','Mé','Xo','Ve','Sá'],
- weekHeader: 'Sm',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['gl']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-he.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-he.js
deleted file mode 100644
index b9e8dee..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-he.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Hebrew initialisation for the UI Datepicker extension. */
-/* Written by Amir Hardon (ahardon at gmail dot com). */
-jQuery(function($){
- $.datepicker.regional['he'] = {
- closeText: 'סגור',
- prevText: '<הקודם',
- nextText: 'הבא>',
- currentText: 'היום',
- monthNames: ['ינואר','פברואר','מרץ','אפריל','מאי','יוני',
- 'יולי','אוגוסט','ספטמבר','אוקטובר','נובמבר','דצמבר'],
- monthNamesShort: ['ינו','פבר','מרץ','אפר','מאי','יוני',
- 'יולי','אוג','ספט','אוק','נוב','דצמ'],
- dayNames: ['ראשון','שני','שלישי','רביעי','חמישי','שישי','שבת'],
- dayNamesShort: ['א\'','ב\'','ג\'','ד\'','ה\'','ו\'','שבת'],
- dayNamesMin: ['א\'','ב\'','ג\'','ד\'','ה\'','ו\'','שבת'],
- weekHeader: 'Wk',
- dateFormat: 'dd/mm/yy',
- firstDay: 0,
- isRTL: true,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['he']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hi.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hi.js
deleted file mode 100644
index 6c563b9..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hi.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Hindi initialisation for the jQuery UI date picker plugin. */
-/* Written by Michael Dawart. */
-jQuery(function($){
- $.datepicker.regional['hi'] = {
- closeText: 'बंद',
- prevText: 'पिछला',
- nextText: 'अगला',
- currentText: 'आज',
- monthNames: ['जनवरी ','फरवरी','मार्च','अप्रेल','मई','जून',
- 'जूलाई','अगस्त ','सितम्बर','अक्टूबर','नवम्बर','दिसम्बर'],
- monthNamesShort: ['जन', 'फर', 'मार्च', 'अप्रेल', 'मई', 'जून',
- 'जूलाई', 'अग', 'सित', 'अक्ट', 'नव', 'दि'],
- dayNames: ['रविवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'गुरुवार', 'शुक्रवार', 'शनिवार'],
- dayNamesShort: ['रवि', 'सोम', 'मंगल', 'बुध', 'गुरु', 'शुक्र', 'शनि'],
- dayNamesMin: ['रवि', 'सोम', 'मंगल', 'बुध', 'गुरु', 'शुक्र', 'शनि'],
- weekHeader: 'हफ्ता',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['hi']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hr.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hr.js
deleted file mode 100644
index 2fe37b6..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hr.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Croatian i18n for the jQuery UI date picker plugin. */
-/* Written by Vjekoslav Nesek. */
-jQuery(function($){
- $.datepicker.regional['hr'] = {
- closeText: 'Zatvori',
- prevText: '<',
- nextText: '>',
- currentText: 'Danas',
- monthNames: ['Siječanj','Veljača','Ožujak','Travanj','Svibanj','Lipanj',
- 'Srpanj','Kolovoz','Rujan','Listopad','Studeni','Prosinac'],
- monthNamesShort: ['Sij','Velj','Ožu','Tra','Svi','Lip',
- 'Srp','Kol','Ruj','Lis','Stu','Pro'],
- dayNames: ['Nedjelja','Ponedjeljak','Utorak','Srijeda','Četvrtak','Petak','Subota'],
- dayNamesShort: ['Ned','Pon','Uto','Sri','Čet','Pet','Sub'],
- dayNamesMin: ['Ne','Po','Ut','Sr','Če','Pe','Su'],
- weekHeader: 'Tje',
- dateFormat: 'dd.mm.yy.',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['hr']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hu.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hu.js
deleted file mode 100644
index b28c268..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hu.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Hungarian initialisation for the jQuery UI date picker plugin. */
-/* Written by Istvan Karaszi (jquery@spam.raszi.hu). */
-jQuery(function($){
- $.datepicker.regional['hu'] = {
- closeText: 'bezár',
- prevText: 'vissza',
- nextText: 'előre',
- currentText: 'ma',
- monthNames: ['Január', 'Február', 'Március', 'Április', 'Május', 'Június',
- 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December'],
- monthNamesShort: ['Jan', 'Feb', 'Már', 'Ápr', 'Máj', 'Jún',
- 'Júl', 'Aug', 'Szep', 'Okt', 'Nov', 'Dec'],
- dayNames: ['Vasárnap', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat'],
- dayNamesShort: ['Vas', 'Hét', 'Ked', 'Sze', 'Csü', 'Pén', 'Szo'],
- dayNamesMin: ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'],
- weekHeader: 'Hét',
- dateFormat: 'yy.mm.dd.',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: true,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['hu']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hy.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hy.js
deleted file mode 100644
index 6d4eca5..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-hy.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Armenian(UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by Levon Zakaryan (levon.zakaryan@gmail.com)*/
-jQuery(function($){
- $.datepicker.regional['hy'] = {
- closeText: 'Փակել',
- prevText: '<Նախ.',
- nextText: 'Հաջ.>',
- currentText: 'Այսօր',
- monthNames: ['Հունվար','Փետրվար','Մարտ','Ապրիլ','Մայիս','Հունիս',
- 'Հուլիս','Օգոստոս','Սեպտեմբեր','Հոկտեմբեր','Նոյեմբեր','Դեկտեմբեր'],
- monthNamesShort: ['Հունվ','Փետր','Մարտ','Ապր','Մայիս','Հունիս',
- 'Հուլ','Օգս','Սեպ','Հոկ','Նոյ','Դեկ'],
- dayNames: ['կիրակի','եկուշաբթի','երեքշաբթի','չորեքշաբթի','հինգշաբթի','ուրբաթ','շաբաթ'],
- dayNamesShort: ['կիր','երկ','երք','չրք','հնգ','ուրբ','շբթ'],
- dayNamesMin: ['կիր','երկ','երք','չրք','հնգ','ուրբ','շբթ'],
- weekHeader: 'ՇԲՏ',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['hy']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-id.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-id.js
deleted file mode 100644
index 6327fa6..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-id.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Indonesian initialisation for the jQuery UI date picker plugin. */
-/* Written by Deden Fathurahman (dedenf@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['id'] = {
- closeText: 'Tutup',
- prevText: '<mundur',
- nextText: 'maju>',
- currentText: 'hari ini',
- monthNames: ['Januari','Februari','Maret','April','Mei','Juni',
- 'Juli','Agustus','September','Oktober','Nopember','Desember'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun',
- 'Jul','Agus','Sep','Okt','Nop','Des'],
- dayNames: ['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu'],
- dayNamesShort: ['Min','Sen','Sel','Rab','kam','Jum','Sab'],
- dayNamesMin: ['Mg','Sn','Sl','Rb','Km','jm','Sb'],
- weekHeader: 'Mg',
- dateFormat: 'dd/mm/yy',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['id']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-is.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-is.js
deleted file mode 100644
index 925341a..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-is.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Icelandic initialisation for the jQuery UI date picker plugin. */
-/* Written by Haukur H. Thorsson (haukur@eskill.is). */
-jQuery(function($){
- $.datepicker.regional['is'] = {
- closeText: 'Loka',
- prevText: '< Fyrri',
- nextText: 'Næsti >',
- currentText: 'Í dag',
- monthNames: ['Janúar','Febrúar','Mars','Apríl','Maí','Júní',
- 'Júlí','Ágúst','September','Október','Nóvember','Desember'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Maí','Jún',
- 'Júl','Ágú','Sep','Okt','Nóv','Des'],
- dayNames: ['Sunnudagur','Mánudagur','Þriðjudagur','Miðvikudagur','Fimmtudagur','Föstudagur','Laugardagur'],
- dayNamesShort: ['Sun','Mán','Þri','Mið','Fim','Fös','Lau'],
- dayNamesMin: ['Su','Má','Þr','Mi','Fi','Fö','La'],
- weekHeader: 'Vika',
- dateFormat: 'dd/mm/yy',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['is']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-it.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-it.js
deleted file mode 100644
index a01f043..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-it.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Italian initialisation for the jQuery UI date picker plugin. */
-/* Written by Antonello Pasella (antonello.pasella@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['it'] = {
- closeText: 'Chiudi',
- prevText: '<Prec',
- nextText: 'Succ>',
- currentText: 'Oggi',
- monthNames: ['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno',
- 'Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre'],
- monthNamesShort: ['Gen','Feb','Mar','Apr','Mag','Giu',
- 'Lug','Ago','Set','Ott','Nov','Dic'],
- dayNames: ['Domenica','Lunedì','Martedì','Mercoledì','Giovedì','Venerdì','Sabato'],
- dayNamesShort: ['Dom','Lun','Mar','Mer','Gio','Ven','Sab'],
- dayNamesMin: ['Do','Lu','Ma','Me','Gi','Ve','Sa'],
- weekHeader: 'Sm',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['it']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ja.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ja.js
deleted file mode 100644
index 4d0b63c..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ja.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Japanese initialisation for the jQuery UI date picker plugin. */
-/* Written by Kentaro SATO (kentaro@ranvis.com). */
-jQuery(function($){
- $.datepicker.regional['ja'] = {
- closeText: '閉じる',
- prevText: '<前',
- nextText: '次>',
- currentText: '今日',
- monthNames: ['1月','2月','3月','4月','5月','6月',
- '7月','8月','9月','10月','11月','12月'],
- monthNamesShort: ['1月','2月','3月','4月','5月','6月',
- '7月','8月','9月','10月','11月','12月'],
- dayNames: ['日曜日','月曜日','火曜日','水曜日','木曜日','金曜日','土曜日'],
- dayNamesShort: ['日','月','火','水','木','金','土'],
- dayNamesMin: ['日','月','火','水','木','金','土'],
- weekHeader: '週',
- dateFormat: 'yy/mm/dd',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: true,
- yearSuffix: '年'};
- $.datepicker.setDefaults($.datepicker.regional['ja']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ka.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ka.js
deleted file mode 100644
index c10658d..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ka.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Georgian (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by Lado Lomidze (lado.lomidze@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['ka'] = {
- closeText: 'დახურვა',
- prevText: '< წინა',
- nextText: 'შემდეგი >',
- currentText: 'დღეს',
- monthNames: ['იანვარი','თებერვალი','მარტი','აპრილი','მაისი','ივნისი', 'ივლისი','აგვისტო','სექტემბერი','ოქტომბერი','ნოემბერი','დეკემბერი'],
- monthNamesShort: ['იან','თებ','მარ','აპრ','მაი','ივნ', 'ივლ','აგვ','სექ','ოქტ','ნოე','დეკ'],
- dayNames: ['კვირა','ორშაბათი','სამშაბათი','ოთხშაბათი','ხუთშაბათი','პარასკევი','შაბათი'],
- dayNamesShort: ['კვ','ორშ','სამ','ოთხ','ხუთ','პარ','შაბ'],
- dayNamesMin: ['კვ','ორშ','სამ','ოთხ','ხუთ','პარ','შაბ'],
- weekHeader: 'კვირა',
- dateFormat: 'dd-mm-yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['ka']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-kk.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-kk.js
deleted file mode 100644
index dcd6a65..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-kk.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Kazakh (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by Dmitriy Karasyov (dmitriy.karasyov@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['kk'] = {
- closeText: 'Жабу',
- prevText: '<Алдыңғы',
- nextText: 'Келесі>',
- currentText: 'Бүгін',
- monthNames: ['Қаңтар','Ақпан','Наурыз','Сәуір','Мамыр','Маусым',
- 'Шілде','Тамыз','Қыркүйек','Қазан','Қараша','Желтоқсан'],
- monthNamesShort: ['Қаң','Ақп','Нау','Сәу','Мам','Мау',
- 'Шіл','Там','Қыр','Қаз','Қар','Жел'],
- dayNames: ['Жексенбі','Дүйсенбі','Сейсенбі','Сәрсенбі','Бейсенбі','Жұма','Сенбі'],
- dayNamesShort: ['жкс','дсн','ссн','срс','бсн','жма','снб'],
- dayNamesMin: ['Жк','Дс','Сс','Ср','Бс','Жм','Сн'],
- weekHeader: 'Не',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['kk']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-km.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-km.js
deleted file mode 100644
index f9c4e3a..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-km.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Khmer initialisation for the jQuery calendar extension. */
-/* Written by Chandara Om (chandara.teacher@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['km'] = {
- closeText: 'ធ្វើរួច',
- prevText: 'មុន',
- nextText: 'បន្ទាប់',
- currentText: 'ថ្ងៃនេះ',
- monthNames: ['មករា','កុម្ភៈ','មីនា','មេសា','ឧសភា','មិថុនា',
- 'កក្កដា','សីហា','កញ្ញា','តុលា','វិច្ឆិកា','ធ្នូ'],
- monthNamesShort: ['មករា','កុម្ភៈ','មីនា','មេសា','ឧសភា','មិថុនា',
- 'កក្កដា','សីហា','កញ្ញា','តុលា','វិច្ឆិកា','ធ្នូ'],
- dayNames: ['អាទិត្យ', 'ចន្ទ', 'អង្គារ', 'ពុធ', 'ព្រហស្បតិ៍', 'សុក្រ', 'សៅរ៍'],
- dayNamesShort: ['អា', 'ច', 'អ', 'ពុ', 'ព្រហ', 'សុ', 'សៅ'],
- dayNamesMin: ['អា', 'ច', 'អ', 'ពុ', 'ព្រហ', 'សុ', 'សៅ'],
- weekHeader: 'សប្ដាហ៍',
- dateFormat: 'dd-mm-yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['km']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ko.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ko.js
deleted file mode 100644
index af36f3d..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ko.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Korean initialisation for the jQuery calendar extension. */
-/* Written by DaeKwon Kang (ncrash.dk@gmail.com), Edited by Genie. */
-jQuery(function($){
- $.datepicker.regional['ko'] = {
- closeText: '닫기',
- prevText: '이전달',
- nextText: '다음달',
- currentText: '오늘',
- monthNames: ['1월','2월','3월','4월','5월','6월',
- '7월','8월','9월','10월','11월','12월'],
- monthNamesShort: ['1월','2월','3월','4월','5월','6월',
- '7월','8월','9월','10월','11월','12월'],
- dayNames: ['일요일','월요일','화요일','수요일','목요일','금요일','토요일'],
- dayNamesShort: ['일','월','화','수','목','금','토'],
- dayNamesMin: ['일','월','화','수','목','금','토'],
- weekHeader: 'Wk',
- dateFormat: 'yy-mm-dd',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: true,
- yearSuffix: '년'};
- $.datepicker.setDefaults($.datepicker.regional['ko']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ky.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ky.js
deleted file mode 100644
index d4466b1..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ky.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Kyrgyz (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by Sergey Kartashov (ebishkek@yandex.ru). */
-jQuery(function($){
- $.datepicker.regional['ky'] = {
- closeText: 'Жабуу',
- prevText: '<Мур',
- nextText: 'Кий>',
- currentText: 'Бүгүн',
- monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь',
- 'Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
- monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн',
- 'Июл','Авг','Сен','Окт','Ноя','Дек'],
- dayNames: ['жекшемби', 'дүйшөмбү', 'шейшемби', 'шаршемби', 'бейшемби', 'жума', 'ишемби'],
- dayNamesShort: ['жек', 'дүй', 'шей', 'шар', 'бей', 'жум', 'ише'],
- dayNamesMin: ['Жк','Дш','Шш','Шр','Бш','Жм','Иш'],
- weekHeader: 'Жум',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''
- };
- $.datepicker.setDefaults($.datepicker.regional['ky']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-lb.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-lb.js
deleted file mode 100644
index 87c79d5..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-lb.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Luxembourgish initialisation for the jQuery UI date picker plugin. */
-/* Written by Michel Weimerskirch */
-jQuery(function($){
- $.datepicker.regional['lb'] = {
- closeText: 'Fäerdeg',
- prevText: 'Zréck',
- nextText: 'Weider',
- currentText: 'Haut',
- monthNames: ['Januar','Februar','Mäerz','Abrëll','Mee','Juni',
- 'Juli','August','September','Oktober','November','Dezember'],
- monthNamesShort: ['Jan', 'Feb', 'Mäe', 'Abr', 'Mee', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
- dayNames: ['Sonndeg', 'Méindeg', 'Dënschdeg', 'Mëttwoch', 'Donneschdeg', 'Freideg', 'Samschdeg'],
- dayNamesShort: ['Son', 'Méi', 'Dën', 'Mët', 'Don', 'Fre', 'Sam'],
- dayNamesMin: ['So','Mé','Dë','Më','Do','Fr','Sa'],
- weekHeader: 'W',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['lb']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-lt.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-lt.js
deleted file mode 100644
index 1afaaac..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-lt.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Lithuanian (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* @author Arturas Paleicikas */
-jQuery(function($){
- $.datepicker.regional['lt'] = {
- closeText: 'Uždaryti',
- prevText: '<Atgal',
- nextText: 'Pirmyn>',
- currentText: 'Šiandien',
- monthNames: ['Sausis','Vasaris','Kovas','Balandis','Gegužė','Birželis',
- 'Liepa','Rugpjūtis','Rugsėjis','Spalis','Lapkritis','Gruodis'],
- monthNamesShort: ['Sau','Vas','Kov','Bal','Geg','Bir',
- 'Lie','Rugp','Rugs','Spa','Lap','Gru'],
- dayNames: ['sekmadienis','pirmadienis','antradienis','trečiadienis','ketvirtadienis','penktadienis','šeštadienis'],
- dayNamesShort: ['sek','pir','ant','tre','ket','pen','šeš'],
- dayNamesMin: ['Se','Pr','An','Tr','Ke','Pe','Še'],
- weekHeader: 'Wk',
- dateFormat: 'yy-mm-dd',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['lt']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-lv.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-lv.js
deleted file mode 100644
index 28cc102..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-lv.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Latvian (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* @author Arturas Paleicikas */
-jQuery(function($){
- $.datepicker.regional['lv'] = {
- closeText: 'Aizvērt',
- prevText: 'Iepr',
- nextText: 'Nāka',
- currentText: 'Šodien',
- monthNames: ['Janvāris','Februāris','Marts','Aprīlis','Maijs','Jūnijs',
- 'Jūlijs','Augusts','Septembris','Oktobris','Novembris','Decembris'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Mai','Jūn',
- 'Jūl','Aug','Sep','Okt','Nov','Dec'],
- dayNames: ['svētdiena','pirmdiena','otrdiena','trešdiena','ceturtdiena','piektdiena','sestdiena'],
- dayNamesShort: ['svt','prm','otr','tre','ctr','pkt','sst'],
- dayNamesMin: ['Sv','Pr','Ot','Tr','Ct','Pk','Ss'],
- weekHeader: 'Nav',
- dateFormat: 'dd-mm-yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['lv']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-mk.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-mk.js
deleted file mode 100644
index 0285325..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-mk.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Macedonian i18n for the jQuery UI date picker plugin. */
-/* Written by Stojce Slavkovski. */
-jQuery(function($){
- $.datepicker.regional['mk'] = {
- closeText: 'Затвори',
- prevText: '<',
- nextText: '>',
- currentText: 'Денес',
- monthNames: ['Јануари','Февруари','Март','Април','Мај','Јуни',
- 'Јули','Август','Септември','Октомври','Ноември','Декември'],
- monthNamesShort: ['Јан','Фев','Мар','Апр','Мај','Јун',
- 'Јул','Авг','Сеп','Окт','Ное','Дек'],
- dayNames: ['Недела','Понеделник','Вторник','Среда','Четврток','Петок','Сабота'],
- dayNamesShort: ['Нед','Пон','Вто','Сре','Чет','Пет','Саб'],
- dayNamesMin: ['Не','По','Вт','Ср','Че','Пе','Са'],
- weekHeader: 'Сед',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['mk']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ml.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ml.js
deleted file mode 100644
index 9b8f460..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ml.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Malayalam (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by Saji Nediyanchath (saji89@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['ml'] = {
- closeText: 'ശരി',
- prevText: 'മുന്നത്തെ',
- nextText: 'അടുത്തത് ',
- currentText: 'ഇന്ന്',
- monthNames: ['ജനുവരി','ഫെബ്രുവരി','മാര്ച്ച്','ഏപ്രില്','മേയ്','ജൂണ്',
- 'ജൂലൈ','ആഗസ്റ്റ്','സെപ്റ്റംബര്','ഒക്ടോബര്','നവംബര്','ഡിസംബര്'],
- monthNamesShort: ['ജനു', 'ഫെബ്', 'മാര്', 'ഏപ്രി', 'മേയ്', 'ജൂണ്',
- 'ജൂലാ', 'ആഗ', 'സെപ്', 'ഒക്ടോ', 'നവം', 'ഡിസ'],
- dayNames: ['ഞായര്', 'തിങ്കള്', 'ചൊവ്വ', 'ബുധന്', 'വ്യാഴം', 'വെള്ളി', 'ശനി'],
- dayNamesShort: ['ഞായ', 'തിങ്ക', 'ചൊവ്വ', 'ബുധ', 'വ്യാഴം', 'വെള്ളി', 'ശനി'],
- dayNamesMin: ['ഞാ','തി','ചൊ','ബു','വ്യാ','വെ','ശ'],
- weekHeader: 'ആ',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['ml']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ms.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ms.js
deleted file mode 100644
index e70de72..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ms.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Malaysian initialisation for the jQuery UI date picker plugin. */
-/* Written by Mohd Nawawi Mohamad Jamili (nawawi@ronggeng.net). */
-jQuery(function($){
- $.datepicker.regional['ms'] = {
- closeText: 'Tutup',
- prevText: '<Sebelum',
- nextText: 'Selepas>',
- currentText: 'hari ini',
- monthNames: ['Januari','Februari','Mac','April','Mei','Jun',
- 'Julai','Ogos','September','Oktober','November','Disember'],
- monthNamesShort: ['Jan','Feb','Mac','Apr','Mei','Jun',
- 'Jul','Ogo','Sep','Okt','Nov','Dis'],
- dayNames: ['Ahad','Isnin','Selasa','Rabu','Khamis','Jumaat','Sabtu'],
- dayNamesShort: ['Aha','Isn','Sel','Rab','kha','Jum','Sab'],
- dayNamesMin: ['Ah','Is','Se','Ra','Kh','Ju','Sa'],
- weekHeader: 'Mg',
- dateFormat: 'dd/mm/yy',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['ms']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nb.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nb.js
deleted file mode 100644
index 845a505..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nb.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Norwegian Bokmål initialisation for the jQuery UI date picker plugin. */
-/* Written by Bjørn Johansen (post@bjornjohansen.no). */
-jQuery(function($){
- $.datepicker.regional['nb'] = {
- closeText: 'Lukk',
- prevText: '«Forrige',
- nextText: 'Neste»',
- currentText: 'I dag',
- monthNames: ['januar','februar','mars','april','mai','juni','juli','august','september','oktober','november','desember'],
- monthNamesShort: ['jan','feb','mar','apr','mai','jun','jul','aug','sep','okt','nov','des'],
- dayNamesShort: ['søn','man','tir','ons','tor','fre','lør'],
- dayNames: ['søndag','mandag','tirsdag','onsdag','torsdag','fredag','lørdag'],
- dayNamesMin: ['sø','ma','ti','on','to','fr','lø'],
- weekHeader: 'Uke',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''
- };
- $.datepicker.setDefaults($.datepicker.regional['nb']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nl-BE.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nl-BE.js
deleted file mode 100644
index 7b3cdf4..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nl-BE.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Dutch (Belgium) initialisation for the jQuery UI date picker plugin. */
-/* David De Sloovere @DavidDeSloovere */
-jQuery(function($){
- $.datepicker.regional['nl-BE'] = {
- closeText: 'Sluiten',
- prevText: '←',
- nextText: '→',
- currentText: 'Vandaag',
- monthNames: ['januari', 'februari', 'maart', 'april', 'mei', 'juni',
- 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
- monthNamesShort: ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun',
- 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
- dayNames: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
- dayNamesShort: ['zon', 'maa', 'din', 'woe', 'don', 'vri', 'zat'],
- dayNamesMin: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
- weekHeader: 'Wk',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['nl-BE']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nl.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nl.js
deleted file mode 100644
index 203f160..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nl.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Dutch (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by Mathias Bynens */
-jQuery(function($){
- $.datepicker.regional.nl = {
- closeText: 'Sluiten',
- prevText: '←',
- nextText: '→',
- currentText: 'Vandaag',
- monthNames: ['januari', 'februari', 'maart', 'april', 'mei', 'juni',
- 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
- monthNamesShort: ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun',
- 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
- dayNames: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
- dayNamesShort: ['zon', 'maa', 'din', 'woe', 'don', 'vri', 'zat'],
- dayNamesMin: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
- weekHeader: 'Wk',
- dateFormat: 'dd-mm-yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional.nl);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nn.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nn.js
deleted file mode 100644
index b55245e..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-nn.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Norwegian Nynorsk initialisation for the jQuery UI date picker plugin. */
-/* Written by Bjørn Johansen (post@bjornjohansen.no). */
-jQuery(function($){
- $.datepicker.regional['nn'] = {
- closeText: 'Lukk',
- prevText: '«Førre',
- nextText: 'Neste»',
- currentText: 'I dag',
- monthNames: ['januar','februar','mars','april','mai','juni','juli','august','september','oktober','november','desember'],
- monthNamesShort: ['jan','feb','mar','apr','mai','jun','jul','aug','sep','okt','nov','des'],
- dayNamesShort: ['sun','mån','tys','ons','tor','fre','lau'],
- dayNames: ['sundag','måndag','tysdag','onsdag','torsdag','fredag','laurdag'],
- dayNamesMin: ['su','må','ty','on','to','fr','la'],
- weekHeader: 'Veke',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''
- };
- $.datepicker.setDefaults($.datepicker.regional['nn']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-no.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-no.js
deleted file mode 100644
index d36e430..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-no.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Norwegian initialisation for the jQuery UI date picker plugin. */
-/* Written by Naimdjon Takhirov (naimdjon@gmail.com). */
-
-jQuery(function($){
- $.datepicker.regional['no'] = {
- closeText: 'Lukk',
- prevText: '«Forrige',
- nextText: 'Neste»',
- currentText: 'I dag',
- monthNames: ['januar','februar','mars','april','mai','juni','juli','august','september','oktober','november','desember'],
- monthNamesShort: ['jan','feb','mar','apr','mai','jun','jul','aug','sep','okt','nov','des'],
- dayNamesShort: ['søn','man','tir','ons','tor','fre','lør'],
- dayNames: ['søndag','mandag','tirsdag','onsdag','torsdag','fredag','lørdag'],
- dayNamesMin: ['sø','ma','ti','on','to','fr','lø'],
- weekHeader: 'Uke',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''
- };
- $.datepicker.setDefaults($.datepicker.regional['no']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-pl.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-pl.js
deleted file mode 100644
index 0ffc515..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-pl.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Polish initialisation for the jQuery UI date picker plugin. */
-/* Written by Jacek Wysocki (jacek.wysocki@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['pl'] = {
- closeText: 'Zamknij',
- prevText: '<Poprzedni',
- nextText: 'Następny>',
- currentText: 'Dziś',
- monthNames: ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec',
- 'Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień'],
- monthNamesShort: ['Sty','Lu','Mar','Kw','Maj','Cze',
- 'Lip','Sie','Wrz','Pa','Lis','Gru'],
- dayNames: ['Niedziela','Poniedziałek','Wtorek','Środa','Czwartek','Piątek','Sobota'],
- dayNamesShort: ['Nie','Pn','Wt','Śr','Czw','Pt','So'],
- dayNamesMin: ['N','Pn','Wt','Śr','Cz','Pt','So'],
- weekHeader: 'Tydz',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['pl']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-pt-BR.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-pt-BR.js
deleted file mode 100644
index 521967e..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-pt-BR.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Brazilian initialisation for the jQuery UI date picker plugin. */
-/* Written by Leonildo Costa Silva (leocsilva@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['pt-BR'] = {
- closeText: 'Fechar',
- prevText: '<Anterior',
- nextText: 'Próximo>',
- currentText: 'Hoje',
- monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho',
- 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
- monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun',
- 'Jul','Ago','Set','Out','Nov','Dez'],
- dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'],
- dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'],
- dayNamesMin: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'],
- weekHeader: 'Sm',
- dateFormat: 'dd/mm/yy',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['pt-BR']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-pt.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-pt.js
deleted file mode 100644
index 999f20e..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-pt.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Portuguese initialisation for the jQuery UI date picker plugin. */
-jQuery(function($){
- $.datepicker.regional['pt'] = {
- closeText: 'Fechar',
- prevText: '<Anterior',
- nextText: 'Seguinte',
- currentText: 'Hoje',
- monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho',
- 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
- monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun',
- 'Jul','Ago','Set','Out','Nov','Dez'],
- dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'],
- dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'],
- dayNamesMin: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'],
- weekHeader: 'Sem',
- dateFormat: 'dd/mm/yy',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['pt']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-rm.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-rm.js
deleted file mode 100644
index 22ed216..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-rm.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Romansh initialisation for the jQuery UI date picker plugin. */
-/* Written by Yvonne Gienal (yvonne.gienal@educa.ch). */
-jQuery(function($){
- $.datepicker.regional['rm'] = {
- closeText: 'Serrar',
- prevText: '<Suandant',
- nextText: 'Precedent>',
- currentText: 'Actual',
- monthNames: ['Schaner','Favrer','Mars','Avrigl','Matg','Zercladur', 'Fanadur','Avust','Settember','October','November','December'],
- monthNamesShort: ['Scha','Fev','Mar','Avr','Matg','Zer', 'Fan','Avu','Sett','Oct','Nov','Dec'],
- dayNames: ['Dumengia','Glindesdi','Mardi','Mesemna','Gievgia','Venderdi','Sonda'],
- dayNamesShort: ['Dum','Gli','Mar','Mes','Gie','Ven','Som'],
- dayNamesMin: ['Du','Gl','Ma','Me','Gi','Ve','So'],
- weekHeader: 'emna',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['rm']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ro.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ro.js
deleted file mode 100644
index a988270..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ro.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Romanian initialisation for the jQuery UI date picker plugin.
- *
- * Written by Edmond L. (ll_edmond@walla.com)
- * and Ionut G. Stan (ionut.g.stan@gmail.com)
- */
-jQuery(function($){
- $.datepicker.regional['ro'] = {
- closeText: 'Închide',
- prevText: '« Luna precedentă',
- nextText: 'Luna următoare »',
- currentText: 'Azi',
- monthNames: ['Ianuarie','Februarie','Martie','Aprilie','Mai','Iunie',
- 'Iulie','August','Septembrie','Octombrie','Noiembrie','Decembrie'],
- monthNamesShort: ['Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun',
- 'Iul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
- dayNames: ['Duminică', 'Luni', 'Marţi', 'Miercuri', 'Joi', 'Vineri', 'Sâmbătă'],
- dayNamesShort: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
- dayNamesMin: ['Du','Lu','Ma','Mi','Jo','Vi','Sâ'],
- weekHeader: 'Săpt',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['ro']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ru.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ru.js
deleted file mode 100644
index a519714..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ru.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Russian (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by Andrew Stromnov (stromnov@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['ru'] = {
- closeText: 'Закрыть',
- prevText: '<Пред',
- nextText: 'След>',
- currentText: 'Сегодня',
- monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь',
- 'Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
- monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн',
- 'Июл','Авг','Сен','Окт','Ноя','Дек'],
- dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
- dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'],
- dayNamesMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],
- weekHeader: 'Нед',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['ru']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sk.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sk.js
deleted file mode 100644
index 0cb76c4..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sk.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Slovak initialisation for the jQuery UI date picker plugin. */
-/* Written by Vojtech Rinik (vojto@hmm.sk). */
-jQuery(function($){
- $.datepicker.regional['sk'] = {
- closeText: 'Zavrieť',
- prevText: '<Predchádzajúci',
- nextText: 'Nasledujúci>',
- currentText: 'Dnes',
- monthNames: ['január','február','marec','apríl','máj','jún',
- 'júl','august','september','október','november','december'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Máj','Jún',
- 'Júl','Aug','Sep','Okt','Nov','Dec'],
- dayNames: ['nedeľa','pondelok','utorok','streda','štvrtok','piatok','sobota'],
- dayNamesShort: ['Ned','Pon','Uto','Str','Štv','Pia','Sob'],
- dayNamesMin: ['Ne','Po','Ut','St','Št','Pia','So'],
- weekHeader: 'Ty',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['sk']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sl.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sl.js
deleted file mode 100644
index 048a47a..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sl.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Slovenian initialisation for the jQuery UI date picker plugin. */
-/* Written by Jaka Jancar (jaka@kubje.org). */
-/* c = č, s = š z = ž C = Č S = Š Z = Ž */
-jQuery(function($){
- $.datepicker.regional['sl'] = {
- closeText: 'Zapri',
- prevText: '<Prejšnji',
- nextText: 'Naslednji>',
- currentText: 'Trenutni',
- monthNames: ['Januar','Februar','Marec','April','Maj','Junij',
- 'Julij','Avgust','September','Oktober','November','December'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun',
- 'Jul','Avg','Sep','Okt','Nov','Dec'],
- dayNames: ['Nedelja','Ponedeljek','Torek','Sreda','Četrtek','Petek','Sobota'],
- dayNamesShort: ['Ned','Pon','Tor','Sre','Čet','Pet','Sob'],
- dayNamesMin: ['Ne','Po','To','Sr','Če','Pe','So'],
- weekHeader: 'Teden',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['sl']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sq.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sq.js
deleted file mode 100644
index d6086a7..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sq.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Albanian initialisation for the jQuery UI date picker plugin. */
-/* Written by Flakron Bytyqi (flakron@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['sq'] = {
- closeText: 'mbylle',
- prevText: '<mbrapa',
- nextText: 'Përpara>',
- currentText: 'sot',
- monthNames: ['Janar','Shkurt','Mars','Prill','Maj','Qershor',
- 'Korrik','Gusht','Shtator','Tetor','Nëntor','Dhjetor'],
- monthNamesShort: ['Jan','Shk','Mar','Pri','Maj','Qer',
- 'Kor','Gus','Sht','Tet','Nën','Dhj'],
- dayNames: ['E Diel','E Hënë','E Martë','E Mërkurë','E Enjte','E Premte','E Shtune'],
- dayNamesShort: ['Di','Hë','Ma','Më','En','Pr','Sh'],
- dayNamesMin: ['Di','Hë','Ma','Më','En','Pr','Sh'],
- weekHeader: 'Ja',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['sq']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sr-SR.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sr-SR.js
deleted file mode 100644
index 810d21d..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sr-SR.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Serbian i18n for the jQuery UI date picker plugin. */
-/* Written by Dejan Dimić. */
-jQuery(function($){
- $.datepicker.regional['sr-SR'] = {
- closeText: 'Zatvori',
- prevText: '<',
- nextText: '>',
- currentText: 'Danas',
- monthNames: ['Januar','Februar','Mart','April','Maj','Jun',
- 'Jul','Avgust','Septembar','Oktobar','Novembar','Decembar'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun',
- 'Jul','Avg','Sep','Okt','Nov','Dec'],
- dayNames: ['Nedelja','Ponedeljak','Utorak','Sreda','Četvrtak','Petak','Subota'],
- dayNamesShort: ['Ned','Pon','Uto','Sre','Čet','Pet','Sub'],
- dayNamesMin: ['Ne','Po','Ut','Sr','Če','Pe','Su'],
- weekHeader: 'Sed',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['sr-SR']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sr.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sr.js
deleted file mode 100644
index 1349a26..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sr.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Serbian i18n for the jQuery UI date picker plugin. */
-/* Written by Dejan Dimić. */
-jQuery(function($){
- $.datepicker.regional['sr'] = {
- closeText: 'Затвори',
- prevText: '<',
- nextText: '>',
- currentText: 'Данас',
- monthNames: ['Јануар','Фебруар','Март','Април','Мај','Јун',
- 'Јул','Август','Септембар','Октобар','Новембар','Децембар'],
- monthNamesShort: ['Јан','Феб','Мар','Апр','Мај','Јун',
- 'Јул','Авг','Сеп','Окт','Нов','Дец'],
- dayNames: ['Недеља','Понедељак','Уторак','Среда','Четвртак','Петак','Субота'],
- dayNamesShort: ['Нед','Пон','Уто','Сре','Чет','Пет','Суб'],
- dayNamesMin: ['Не','По','Ут','Ср','Че','Пе','Су'],
- weekHeader: 'Сед',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['sr']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sv.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sv.js
deleted file mode 100644
index cbb5ad1..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-sv.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Swedish initialisation for the jQuery UI date picker plugin. */
-/* Written by Anders Ekdahl ( anders@nomadiz.se). */
-jQuery(function($){
- $.datepicker.regional['sv'] = {
- closeText: 'Stäng',
- prevText: '«Förra',
- nextText: 'Nästa»',
- currentText: 'Idag',
- monthNames: ['Januari','Februari','Mars','April','Maj','Juni',
- 'Juli','Augusti','September','Oktober','November','December'],
- monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun',
- 'Jul','Aug','Sep','Okt','Nov','Dec'],
- dayNamesShort: ['Sön','Mån','Tis','Ons','Tor','Fre','Lör'],
- dayNames: ['Söndag','Måndag','Tisdag','Onsdag','Torsdag','Fredag','Lördag'],
- dayNamesMin: ['Sö','Må','Ti','On','To','Fr','Lö'],
- weekHeader: 'Ve',
- dateFormat: 'yy-mm-dd',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['sv']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ta.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ta.js
deleted file mode 100644
index 40431ed..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-ta.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Tamil (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by S A Sureshkumar (saskumar@live.com). */
-jQuery(function($){
- $.datepicker.regional['ta'] = {
- closeText: 'மூடு',
- prevText: 'முன்னையது',
- nextText: 'அடுத்தது',
- currentText: 'இன்று',
- monthNames: ['தை','மாசி','பங்குனி','சித்திரை','வைகாசி','ஆனி',
- 'ஆடி','ஆவணி','புரட்டாசி','ஐப்பசி','கார்த்திகை','மார்கழி'],
- monthNamesShort: ['தை','மாசி','பங்','சித்','வைகா','ஆனி',
- 'ஆடி','ஆவ','புர','ஐப்','கார்','மார்'],
- dayNames: ['ஞாயிற்றுக்கிழமை','திங்கட்கிழமை','செவ்வாய்க்கிழமை','புதன்கிழமை','வியாழக்கிழமை','வெள்ளிக்கிழமை','சனிக்கிழமை'],
- dayNamesShort: ['ஞாயிறு','திங்கள்','செவ்வாய்','புதன்','வியாழன்','வெள்ளி','சனி'],
- dayNamesMin: ['ஞா','தி','செ','பு','வி','வெ','ச'],
- weekHeader: 'Не',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['ta']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-th.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-th.js
deleted file mode 100644
index aecfd27..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-th.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Thai initialisation for the jQuery UI date picker plugin. */
-/* Written by pipo (pipo@sixhead.com). */
-jQuery(function($){
- $.datepicker.regional['th'] = {
- closeText: 'ปิด',
- prevText: '« ย้อน',
- nextText: 'ถัดไป »',
- currentText: 'วันนี้',
- monthNames: ['มกราคม','กุมภาพันธ์','มีนาคม','เมษายน','พฤษภาคม','มิถุนายน',
- 'กรกฎาคม','สิงหาคม','กันยายน','ตุลาคม','พฤศจิกายน','ธันวาคม'],
- monthNamesShort: ['ม.ค.','ก.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.',
- 'ก.ค.','ส.ค.','ก.ย.','ต.ค.','พ.ย.','ธ.ค.'],
- dayNames: ['อาทิตย์','จันทร์','อังคาร','พุธ','พฤหัสบดี','ศุกร์','เสาร์'],
- dayNamesShort: ['อา.','จ.','อ.','พ.','พฤ.','ศ.','ส.'],
- dayNamesMin: ['อา.','จ.','อ.','พ.','พฤ.','ศ.','ส.'],
- weekHeader: 'Wk',
- dateFormat: 'dd/mm/yy',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['th']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-tj.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-tj.js
deleted file mode 100644
index 9a20e4d..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-tj.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Tajiki (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by Abdurahmon Saidov (saidovab@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['tj'] = {
- closeText: 'Идома',
- prevText: '<Қафо',
- nextText: 'Пеш>',
- currentText: 'Имрӯз',
- monthNames: ['Январ','Феврал','Март','Апрел','Май','Июн',
- 'Июл','Август','Сентябр','Октябр','Ноябр','Декабр'],
- monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн',
- 'Июл','Авг','Сен','Окт','Ноя','Дек'],
- dayNames: ['якшанбе','душанбе','сешанбе','чоршанбе','панҷшанбе','ҷумъа','шанбе'],
- dayNamesShort: ['якш','душ','сеш','чор','пан','ҷум','шан'],
- dayNamesMin: ['Як','Дш','Сш','Чш','Пш','Ҷм','Шн'],
- weekHeader: 'Хф',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['tj']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-tr.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-tr.js
deleted file mode 100644
index 75b583a..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-tr.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Turkish initialisation for the jQuery UI date picker plugin. */
-/* Written by Izzet Emre Erkan (kara@karalamalar.net). */
-jQuery(function($){
- $.datepicker.regional['tr'] = {
- closeText: 'kapat',
- prevText: '<geri',
- nextText: 'ileri>',
- currentText: 'bugün',
- monthNames: ['Ocak','Şubat','Mart','Nisan','Mayıs','Haziran',
- 'Temmuz','Ağustos','Eylül','Ekim','Kasım','Aralık'],
- monthNamesShort: ['Oca','Şub','Mar','Nis','May','Haz',
- 'Tem','Ağu','Eyl','Eki','Kas','Ara'],
- dayNames: ['Pazar','Pazartesi','Salı','Çarşamba','Perşembe','Cuma','Cumartesi'],
- dayNamesShort: ['Pz','Pt','Sa','Ça','Pe','Cu','Ct'],
- dayNamesMin: ['Pz','Pt','Sa','Ça','Pe','Cu','Ct'],
- weekHeader: 'Hf',
- dateFormat: 'dd.mm.yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['tr']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-uk.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-uk.js
deleted file mode 100644
index 2bdc82f..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-uk.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Ukrainian (UTF-8) initialisation for the jQuery UI date picker plugin. */
-/* Written by Maxim Drogobitskiy (maxdao@gmail.com). */
-/* Corrected by Igor Milla (igor.fsp.milla@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['uk'] = {
- closeText: 'Закрити',
- prevText: '<',
- nextText: '>',
- currentText: 'Сьогодні',
- monthNames: ['Січень','Лютий','Березень','Квітень','Травень','Червень',
- 'Липень','Серпень','Вересень','Жовтень','Листопад','Грудень'],
- monthNamesShort: ['Січ','Лют','Бер','Кві','Тра','Чер',
- 'Лип','Сер','Вер','Жов','Лис','Гру'],
- dayNames: ['неділя','понеділок','вівторок','середа','четвер','п’ятниця','субота'],
- dayNamesShort: ['нед','пнд','вів','срд','чтв','птн','сбт'],
- dayNamesMin: ['Нд','Пн','Вт','Ср','Чт','Пт','Сб'],
- weekHeader: 'Тиж',
- dateFormat: 'dd/mm/yy',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['uk']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-vi.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-vi.js
deleted file mode 100644
index b49e7eb..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-vi.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Vietnamese initialisation for the jQuery UI date picker plugin. */
-/* Translated by Le Thanh Huy (lthanhhuy@cit.ctu.edu.vn). */
-jQuery(function($){
- $.datepicker.regional['vi'] = {
- closeText: 'Đóng',
- prevText: '<Trước',
- nextText: 'Tiếp>',
- currentText: 'Hôm nay',
- monthNames: ['Tháng Một', 'Tháng Hai', 'Tháng Ba', 'Tháng Tư', 'Tháng Năm', 'Tháng Sáu',
- 'Tháng Bảy', 'Tháng Tám', 'Tháng Chín', 'Tháng Mười', 'Tháng Mười Một', 'Tháng Mười Hai'],
- monthNamesShort: ['Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6',
- 'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12'],
- dayNames: ['Chủ Nhật', 'Thứ Hai', 'Thứ Ba', 'Thứ Tư', 'Thứ Năm', 'Thứ Sáu', 'Thứ Bảy'],
- dayNamesShort: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],
- dayNamesMin: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],
- weekHeader: 'Tu',
- dateFormat: 'dd/mm/yy',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: ''};
- $.datepicker.setDefaults($.datepicker.regional['vi']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-zh-CN.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-zh-CN.js
deleted file mode 100644
index d337e4a..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-zh-CN.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Chinese initialisation for the jQuery UI date picker plugin. */
-/* Written by Cloudream (cloudream@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['zh-CN'] = {
- closeText: '关闭',
- prevText: '<上月',
- nextText: '下月>',
- currentText: '今天',
- monthNames: ['一月','二月','三月','四月','五月','六月',
- '七月','八月','九月','十月','十一月','十二月'],
- monthNamesShort: ['一月','二月','三月','四月','五月','六月',
- '七月','八月','九月','十月','十一月','十二月'],
- dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'],
- dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'],
- dayNamesMin: ['日','一','二','三','四','五','六'],
- weekHeader: '周',
- dateFormat: 'yy-mm-dd',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: true,
- yearSuffix: '年'};
- $.datepicker.setDefaults($.datepicker.regional['zh-CN']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-zh-HK.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-zh-HK.js
deleted file mode 100644
index ef6f4e7..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-zh-HK.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Chinese initialisation for the jQuery UI date picker plugin. */
-/* Written by SCCY (samuelcychan@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['zh-HK'] = {
- closeText: '關閉',
- prevText: '<上月',
- nextText: '下月>',
- currentText: '今天',
- monthNames: ['一月','二月','三月','四月','五月','六月',
- '七月','八月','九月','十月','十一月','十二月'],
- monthNamesShort: ['一月','二月','三月','四月','五月','六月',
- '七月','八月','九月','十月','十一月','十二月'],
- dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'],
- dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'],
- dayNamesMin: ['日','一','二','三','四','五','六'],
- weekHeader: '周',
- dateFormat: 'dd-mm-yy',
- firstDay: 0,
- isRTL: false,
- showMonthAfterYear: true,
- yearSuffix: '年'};
- $.datepicker.setDefaults($.datepicker.regional['zh-HK']);
-});
diff --git a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-zh-TW.js b/framework/yii/jui/assets/i18n/jquery.ui.datepicker-zh-TW.js
deleted file mode 100644
index b9105ea..0000000
--- a/framework/yii/jui/assets/i18n/jquery.ui.datepicker-zh-TW.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Chinese initialisation for the jQuery UI date picker plugin. */
-/* Written by Ressol (ressol@gmail.com). */
-jQuery(function($){
- $.datepicker.regional['zh-TW'] = {
- closeText: '關閉',
- prevText: '<上月',
- nextText: '下月>',
- currentText: '今天',
- monthNames: ['一月','二月','三月','四月','五月','六月',
- '七月','八月','九月','十月','十一月','十二月'],
- monthNamesShort: ['一月','二月','三月','四月','五月','六月',
- '七月','八月','九月','十月','十一月','十二月'],
- dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'],
- dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'],
- dayNamesMin: ['日','一','二','三','四','五','六'],
- weekHeader: '周',
- dateFormat: 'yy/mm/dd',
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: true,
- yearSuffix: '年'};
- $.datepicker.setDefaults($.datepicker.regional['zh-TW']);
-});
diff --git a/framework/yii/jui/assets/jquery.ui.effect-blind.js b/framework/yii/jui/assets/jquery.ui.effect-blind.js
deleted file mode 100644
index 83f7497..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-blind.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/*!
- * jQuery UI Effects Blind 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/blind-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-var rvertical = /up|down|vertical/,
- rpositivemotion = /up|left|vertical|horizontal/;
-
-$.effects.effect.blind = function( o, done ) {
- // Create element
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
- mode = $.effects.setMode( el, o.mode || "hide" ),
- direction = o.direction || "up",
- vertical = rvertical.test( direction ),
- ref = vertical ? "height" : "width",
- ref2 = vertical ? "top" : "left",
- motion = rpositivemotion.test( direction ),
- animation = {},
- show = mode === "show",
- wrapper, distance, margin;
-
- // if already wrapped, the wrapper's properties are my property. #6245
- if ( el.parent().is( ".ui-effects-wrapper" ) ) {
- $.effects.save( el.parent(), props );
- } else {
- $.effects.save( el, props );
- }
- el.show();
- wrapper = $.effects.createWrapper( el ).css({
- overflow: "hidden"
- });
-
- distance = wrapper[ ref ]();
- margin = parseFloat( wrapper.css( ref2 ) ) || 0;
-
- animation[ ref ] = show ? distance : 0;
- if ( !motion ) {
- el
- .css( vertical ? "bottom" : "right", 0 )
- .css( vertical ? "top" : "left", "auto" )
- .css({ position: "absolute" });
-
- animation[ ref2 ] = show ? margin : distance + margin;
- }
-
- // start at 0 if we are showing
- if ( show ) {
- wrapper.css( ref, 0 );
- if ( ! motion ) {
- wrapper.css( ref2, margin + distance );
- }
- }
-
- // Animate
- wrapper.animate( animation, {
- duration: o.duration,
- easing: o.easing,
- queue: false,
- complete: function() {
- if ( mode === "hide" ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- }
- });
-
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-bounce.js b/framework/yii/jui/assets/jquery.ui.effect-bounce.js
deleted file mode 100644
index e518a1e..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-bounce.js
+++ /dev/null
@@ -1,113 +0,0 @@
-/*!
- * jQuery UI Effects Bounce 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/bounce-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.bounce = function( o, done ) {
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
-
- // defaults:
- mode = $.effects.setMode( el, o.mode || "effect" ),
- hide = mode === "hide",
- show = mode === "show",
- direction = o.direction || "up",
- distance = o.distance,
- times = o.times || 5,
-
- // number of internal animations
- anims = times * 2 + ( show || hide ? 1 : 0 ),
- speed = o.duration / anims,
- easing = o.easing,
-
- // utility:
- ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
- motion = ( direction === "up" || direction === "left" ),
- i,
- upAnim,
- downAnim,
-
- // we will need to re-assemble the queue to stack our animations in place
- queue = el.queue(),
- queuelen = queue.length;
-
- // Avoid touching opacity to prevent clearType and PNG issues in IE
- if ( show || hide ) {
- props.push( "opacity" );
- }
-
- $.effects.save( el, props );
- el.show();
- $.effects.createWrapper( el ); // Create Wrapper
-
- // default distance for the BIGGEST bounce is the outer Distance / 3
- if ( !distance ) {
- distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
- }
-
- if ( show ) {
- downAnim = { opacity: 1 };
- downAnim[ ref ] = 0;
-
- // if we are showing, force opacity 0 and set the initial position
- // then do the "first" animation
- el.css( "opacity", 0 )
- .css( ref, motion ? -distance * 2 : distance * 2 )
- .animate( downAnim, speed, easing );
- }
-
- // start at the smallest distance if we are hiding
- if ( hide ) {
- distance = distance / Math.pow( 2, times - 1 );
- }
-
- downAnim = {};
- downAnim[ ref ] = 0;
- // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
- for ( i = 0; i < times; i++ ) {
- upAnim = {};
- upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
-
- el.animate( upAnim, speed, easing )
- .animate( downAnim, speed, easing );
-
- distance = hide ? distance * 2 : distance / 2;
- }
-
- // Last Bounce when Hiding
- if ( hide ) {
- upAnim = { opacity: 0 };
- upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
-
- el.animate( upAnim, speed, easing );
- }
-
- el.queue(function() {
- if ( hide ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- });
-
- // inject all the animations we just queued to be first in line (after "inprogress")
- if ( queuelen > 1) {
- queue.splice.apply( queue,
- [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
- }
- el.dequeue();
-
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-clip.js b/framework/yii/jui/assets/jquery.ui.effect-clip.js
deleted file mode 100644
index 4c2c2dc..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-clip.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/*!
- * jQuery UI Effects Clip 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/clip-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.clip = function( o, done ) {
- // Create element
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
- mode = $.effects.setMode( el, o.mode || "hide" ),
- show = mode === "show",
- direction = o.direction || "vertical",
- vert = direction === "vertical",
- size = vert ? "height" : "width",
- position = vert ? "top" : "left",
- animation = {},
- wrapper, animate, distance;
-
- // Save & Show
- $.effects.save( el, props );
- el.show();
-
- // Create Wrapper
- wrapper = $.effects.createWrapper( el ).css({
- overflow: "hidden"
- });
- animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
- distance = animate[ size ]();
-
- // Shift
- if ( show ) {
- animate.css( size, 0 );
- animate.css( position, distance / 2 );
- }
-
- // Create Animation Object:
- animation[ size ] = show ? distance : 0;
- animation[ position ] = show ? 0 : distance / 2;
-
- // Animate
- animate.animate( animation, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: function() {
- if ( !show ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- }
- });
-
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-drop.js b/framework/yii/jui/assets/jquery.ui.effect-drop.js
deleted file mode 100644
index 21c0737..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-drop.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/*!
- * jQuery UI Effects Drop 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/drop-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.drop = function( o, done ) {
-
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
- mode = $.effects.setMode( el, o.mode || "hide" ),
- show = mode === "show",
- direction = o.direction || "left",
- ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
- motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
- animation = {
- opacity: show ? 1 : 0
- },
- distance;
-
- // Adjust
- $.effects.save( el, props );
- el.show();
- $.effects.createWrapper( el );
-
- distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
-
- if ( show ) {
- el
- .css( "opacity", 0 )
- .css( ref, motion === "pos" ? -distance : distance );
- }
-
- // Animation
- animation[ ref ] = ( show ?
- ( motion === "pos" ? "+=" : "-=" ) :
- ( motion === "pos" ? "-=" : "+=" ) ) +
- distance;
-
- // Animate
- el.animate( animation, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: function() {
- if ( mode === "hide" ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- }
- });
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-explode.js b/framework/yii/jui/assets/jquery.ui.effect-explode.js
deleted file mode 100644
index f60af96..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-explode.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/*!
- * jQuery UI Effects Explode 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/explode-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.explode = function( o, done ) {
-
- var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
- cells = rows,
- el = $( this ),
- mode = $.effects.setMode( el, o.mode || "hide" ),
- show = mode === "show",
-
- // show and then visibility:hidden the element before calculating offset
- offset = el.show().css( "visibility", "hidden" ).offset(),
-
- // width and height of a piece
- width = Math.ceil( el.outerWidth() / cells ),
- height = Math.ceil( el.outerHeight() / rows ),
- pieces = [],
-
- // loop
- i, j, left, top, mx, my;
-
- // children animate complete:
- function childComplete() {
- pieces.push( this );
- if ( pieces.length === rows * cells ) {
- animComplete();
- }
- }
-
- // clone the element for each row and cell.
- for( i = 0; i < rows ; i++ ) { // ===>
- top = offset.top + i * height;
- my = i - ( rows - 1 ) / 2 ;
-
- for( j = 0; j < cells ; j++ ) { // |||
- left = offset.left + j * width;
- mx = j - ( cells - 1 ) / 2 ;
-
- // Create a clone of the now hidden main element that will be absolute positioned
- // within a wrapper div off the -left and -top equal to size of our pieces
- el
- .clone()
- .appendTo( "body" )
- .wrap( "
" )
- .css({
- position: "absolute",
- visibility: "visible",
- left: -j * width,
- top: -i * height
- })
-
- // select the wrapper - make it overflow: hidden and absolute positioned based on
- // where the original was located +left and +top equal to the size of pieces
- .parent()
- .addClass( "ui-effects-explode" )
- .css({
- position: "absolute",
- overflow: "hidden",
- width: width,
- height: height,
- left: left + ( show ? mx * width : 0 ),
- top: top + ( show ? my * height : 0 ),
- opacity: show ? 0 : 1
- }).animate({
- left: left + ( show ? 0 : mx * width ),
- top: top + ( show ? 0 : my * height ),
- opacity: show ? 1 : 0
- }, o.duration || 500, o.easing, childComplete );
- }
- }
-
- function animComplete() {
- el.css({
- visibility: "visible"
- });
- $( pieces ).remove();
- if ( !show ) {
- el.hide();
- }
- done();
- }
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-fade.js b/framework/yii/jui/assets/jquery.ui.effect-fade.js
deleted file mode 100644
index aaad697..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-fade.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/*!
- * jQuery UI Effects Fade 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/fade-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.fade = function( o, done ) {
- var el = $( this ),
- mode = $.effects.setMode( el, o.mode || "toggle" );
-
- el.animate({
- opacity: mode
- }, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: done
- });
-};
-
-})( jQuery );
diff --git a/framework/yii/jui/assets/jquery.ui.effect-fold.js b/framework/yii/jui/assets/jquery.ui.effect-fold.js
deleted file mode 100644
index c4273b8..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-fold.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*!
- * jQuery UI Effects Fold 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/fold-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.fold = function( o, done ) {
-
- // Create element
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
- mode = $.effects.setMode( el, o.mode || "hide" ),
- show = mode === "show",
- hide = mode === "hide",
- size = o.size || 15,
- percent = /([0-9]+)%/.exec( size ),
- horizFirst = !!o.horizFirst,
- widthFirst = show !== horizFirst,
- ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
- duration = o.duration / 2,
- wrapper, distance,
- animation1 = {},
- animation2 = {};
-
- $.effects.save( el, props );
- el.show();
-
- // Create Wrapper
- wrapper = $.effects.createWrapper( el ).css({
- overflow: "hidden"
- });
- distance = widthFirst ?
- [ wrapper.width(), wrapper.height() ] :
- [ wrapper.height(), wrapper.width() ];
-
- if ( percent ) {
- size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
- }
- if ( show ) {
- wrapper.css( horizFirst ? {
- height: 0,
- width: size
- } : {
- height: size,
- width: 0
- });
- }
-
- // Animation
- animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
- animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
-
- // Animate
- wrapper
- .animate( animation1, duration, o.easing )
- .animate( animation2, duration, o.easing, function() {
- if ( hide ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- });
-
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-highlight.js b/framework/yii/jui/assets/jquery.ui.effect-highlight.js
deleted file mode 100644
index 6b9438c..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-highlight.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*!
- * jQuery UI Effects Highlight 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/highlight-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.highlight = function( o, done ) {
- var elem = $( this ),
- props = [ "backgroundImage", "backgroundColor", "opacity" ],
- mode = $.effects.setMode( elem, o.mode || "show" ),
- animation = {
- backgroundColor: elem.css( "backgroundColor" )
- };
-
- if (mode === "hide") {
- animation.opacity = 0;
- }
-
- $.effects.save( elem, props );
-
- elem
- .show()
- .css({
- backgroundImage: "none",
- backgroundColor: o.color || "#ffff99"
- })
- .animate( animation, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: function() {
- if ( mode === "hide" ) {
- elem.hide();
- }
- $.effects.restore( elem, props );
- done();
- }
- });
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-pulsate.js b/framework/yii/jui/assets/jquery.ui.effect-pulsate.js
deleted file mode 100644
index b357f1a..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-pulsate.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/*!
- * jQuery UI Effects Pulsate 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/pulsate-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.pulsate = function( o, done ) {
- var elem = $( this ),
- mode = $.effects.setMode( elem, o.mode || "show" ),
- show = mode === "show",
- hide = mode === "hide",
- showhide = ( show || mode === "hide" ),
-
- // showing or hiding leaves of the "last" animation
- anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
- duration = o.duration / anims,
- animateTo = 0,
- queue = elem.queue(),
- queuelen = queue.length,
- i;
-
- if ( show || !elem.is(":visible")) {
- elem.css( "opacity", 0 ).show();
- animateTo = 1;
- }
-
- // anims - 1 opacity "toggles"
- for ( i = 1; i < anims; i++ ) {
- elem.animate({
- opacity: animateTo
- }, duration, o.easing );
- animateTo = 1 - animateTo;
- }
-
- elem.animate({
- opacity: animateTo
- }, duration, o.easing);
-
- elem.queue(function() {
- if ( hide ) {
- elem.hide();
- }
- done();
- });
-
- // We just queued up "anims" animations, we need to put them next in the queue
- if ( queuelen > 1 ) {
- queue.splice.apply( queue,
- [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
- }
- elem.dequeue();
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-scale.js b/framework/yii/jui/assets/jquery.ui.effect-scale.js
deleted file mode 100644
index d35224c..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-scale.js
+++ /dev/null
@@ -1,318 +0,0 @@
-/*!
- * jQuery UI Effects Scale 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/scale-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.puff = function( o, done ) {
- var elem = $( this ),
- mode = $.effects.setMode( elem, o.mode || "hide" ),
- hide = mode === "hide",
- percent = parseInt( o.percent, 10 ) || 150,
- factor = percent / 100,
- original = {
- height: elem.height(),
- width: elem.width(),
- outerHeight: elem.outerHeight(),
- outerWidth: elem.outerWidth()
- };
-
- $.extend( o, {
- effect: "scale",
- queue: false,
- fade: true,
- mode: mode,
- complete: done,
- percent: hide ? percent : 100,
- from: hide ?
- original :
- {
- height: original.height * factor,
- width: original.width * factor,
- outerHeight: original.outerHeight * factor,
- outerWidth: original.outerWidth * factor
- }
- });
-
- elem.effect( o );
-};
-
-$.effects.effect.scale = function( o, done ) {
-
- // Create element
- var el = $( this ),
- options = $.extend( true, {}, o ),
- mode = $.effects.setMode( el, o.mode || "effect" ),
- percent = parseInt( o.percent, 10 ) ||
- ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
- direction = o.direction || "both",
- origin = o.origin,
- original = {
- height: el.height(),
- width: el.width(),
- outerHeight: el.outerHeight(),
- outerWidth: el.outerWidth()
- },
- factor = {
- y: direction !== "horizontal" ? (percent / 100) : 1,
- x: direction !== "vertical" ? (percent / 100) : 1
- };
-
- // We are going to pass this effect to the size effect:
- options.effect = "size";
- options.queue = false;
- options.complete = done;
-
- // Set default origin and restore for show/hide
- if ( mode !== "effect" ) {
- options.origin = origin || ["middle","center"];
- options.restore = true;
- }
-
- options.from = o.from || ( mode === "show" ? {
- height: 0,
- width: 0,
- outerHeight: 0,
- outerWidth: 0
- } : original );
- options.to = {
- height: original.height * factor.y,
- width: original.width * factor.x,
- outerHeight: original.outerHeight * factor.y,
- outerWidth: original.outerWidth * factor.x
- };
-
- // Fade option to support puff
- if ( options.fade ) {
- if ( mode === "show" ) {
- options.from.opacity = 0;
- options.to.opacity = 1;
- }
- if ( mode === "hide" ) {
- options.from.opacity = 1;
- options.to.opacity = 0;
- }
- }
-
- // Animate
- el.effect( options );
-
-};
-
-$.effects.effect.size = function( o, done ) {
-
- // Create element
- var original, baseline, factor,
- el = $( this ),
- props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
-
- // Always restore
- props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
-
- // Copy for children
- props2 = [ "width", "height", "overflow" ],
- cProps = [ "fontSize" ],
- vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
- hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
-
- // Set options
- mode = $.effects.setMode( el, o.mode || "effect" ),
- restore = o.restore || mode !== "effect",
- scale = o.scale || "both",
- origin = o.origin || [ "middle", "center" ],
- position = el.css( "position" ),
- props = restore ? props0 : props1,
- zero = {
- height: 0,
- width: 0,
- outerHeight: 0,
- outerWidth: 0
- };
-
- if ( mode === "show" ) {
- el.show();
- }
- original = {
- height: el.height(),
- width: el.width(),
- outerHeight: el.outerHeight(),
- outerWidth: el.outerWidth()
- };
-
- if ( o.mode === "toggle" && mode === "show" ) {
- el.from = o.to || zero;
- el.to = o.from || original;
- } else {
- el.from = o.from || ( mode === "show" ? zero : original );
- el.to = o.to || ( mode === "hide" ? zero : original );
- }
-
- // Set scaling factor
- factor = {
- from: {
- y: el.from.height / original.height,
- x: el.from.width / original.width
- },
- to: {
- y: el.to.height / original.height,
- x: el.to.width / original.width
- }
- };
-
- // Scale the css box
- if ( scale === "box" || scale === "both" ) {
-
- // Vertical props scaling
- if ( factor.from.y !== factor.to.y ) {
- props = props.concat( vProps );
- el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
- el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
- }
-
- // Horizontal props scaling
- if ( factor.from.x !== factor.to.x ) {
- props = props.concat( hProps );
- el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
- el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
- }
- }
-
- // Scale the content
- if ( scale === "content" || scale === "both" ) {
-
- // Vertical props scaling
- if ( factor.from.y !== factor.to.y ) {
- props = props.concat( cProps ).concat( props2 );
- el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
- el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
- }
- }
-
- $.effects.save( el, props );
- el.show();
- $.effects.createWrapper( el );
- el.css( "overflow", "hidden" ).css( el.from );
-
- // Adjust
- if (origin) { // Calculate baseline shifts
- baseline = $.effects.getBaseline( origin, original );
- el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
- el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
- el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
- el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
- }
- el.css( el.from ); // set top & left
-
- // Animate
- if ( scale === "content" || scale === "both" ) { // Scale the children
-
- // Add margins/font-size
- vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
- hProps = hProps.concat([ "marginLeft", "marginRight" ]);
- props2 = props0.concat(vProps).concat(hProps);
-
- el.find( "*[width]" ).each( function(){
- var child = $( this ),
- c_original = {
- height: child.height(),
- width: child.width(),
- outerHeight: child.outerHeight(),
- outerWidth: child.outerWidth()
- };
- if (restore) {
- $.effects.save(child, props2);
- }
-
- child.from = {
- height: c_original.height * factor.from.y,
- width: c_original.width * factor.from.x,
- outerHeight: c_original.outerHeight * factor.from.y,
- outerWidth: c_original.outerWidth * factor.from.x
- };
- child.to = {
- height: c_original.height * factor.to.y,
- width: c_original.width * factor.to.x,
- outerHeight: c_original.height * factor.to.y,
- outerWidth: c_original.width * factor.to.x
- };
-
- // Vertical props scaling
- if ( factor.from.y !== factor.to.y ) {
- child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
- child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
- }
-
- // Horizontal props scaling
- if ( factor.from.x !== factor.to.x ) {
- child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
- child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
- }
-
- // Animate children
- child.css( child.from );
- child.animate( child.to, o.duration, o.easing, function() {
-
- // Restore children
- if ( restore ) {
- $.effects.restore( child, props2 );
- }
- });
- });
- }
-
- // Animate
- el.animate( el.to, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: function() {
- if ( el.to.opacity === 0 ) {
- el.css( "opacity", el.from.opacity );
- }
- if( mode === "hide" ) {
- el.hide();
- }
- $.effects.restore( el, props );
- if ( !restore ) {
-
- // we need to calculate our new positioning based on the scaling
- if ( position === "static" ) {
- el.css({
- position: "relative",
- top: el.to.top,
- left: el.to.left
- });
- } else {
- $.each([ "top", "left" ], function( idx, pos ) {
- el.css( pos, function( _, str ) {
- var val = parseInt( str, 10 ),
- toRef = idx ? el.to.left : el.to.top;
-
- // if original was "auto", recalculate the new value from wrapper
- if ( str === "auto" ) {
- return toRef + "px";
- }
-
- return val + toRef + "px";
- });
- });
- }
- }
-
- $.effects.removeWrapper( el );
- done();
- }
- });
-
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-shake.js b/framework/yii/jui/assets/jquery.ui.effect-shake.js
deleted file mode 100644
index fc12fca..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-shake.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/*!
- * jQuery UI Effects Shake 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/shake-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.shake = function( o, done ) {
-
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
- mode = $.effects.setMode( el, o.mode || "effect" ),
- direction = o.direction || "left",
- distance = o.distance || 20,
- times = o.times || 3,
- anims = times * 2 + 1,
- speed = Math.round(o.duration/anims),
- ref = (direction === "up" || direction === "down") ? "top" : "left",
- positiveMotion = (direction === "up" || direction === "left"),
- animation = {},
- animation1 = {},
- animation2 = {},
- i,
-
- // we will need to re-assemble the queue to stack our animations in place
- queue = el.queue(),
- queuelen = queue.length;
-
- $.effects.save( el, props );
- el.show();
- $.effects.createWrapper( el );
-
- // Animation
- animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
- animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
- animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
-
- // Animate
- el.animate( animation, speed, o.easing );
-
- // Shakes
- for ( i = 1; i < times; i++ ) {
- el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
- }
- el
- .animate( animation1, speed, o.easing )
- .animate( animation, speed / 2, o.easing )
- .queue(function() {
- if ( mode === "hide" ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- });
-
- // inject all the animations we just queued to be first in line (after "inprogress")
- if ( queuelen > 1) {
- queue.splice.apply( queue,
- [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
- }
- el.dequeue();
-
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-slide.js b/framework/yii/jui/assets/jquery.ui.effect-slide.js
deleted file mode 100644
index 0737b86..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-slide.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*!
- * jQuery UI Effects Slide 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/slide-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.slide = function( o, done ) {
-
- // Create element
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
- mode = $.effects.setMode( el, o.mode || "show" ),
- show = mode === "show",
- direction = o.direction || "left",
- ref = (direction === "up" || direction === "down") ? "top" : "left",
- positiveMotion = (direction === "up" || direction === "left"),
- distance,
- animation = {};
-
- // Adjust
- $.effects.save( el, props );
- el.show();
- distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
-
- $.effects.createWrapper( el ).css({
- overflow: "hidden"
- });
-
- if ( show ) {
- el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
- }
-
- // Animation
- animation[ ref ] = ( show ?
- ( positiveMotion ? "+=" : "-=") :
- ( positiveMotion ? "-=" : "+=")) +
- distance;
-
- // Animate
- el.animate( animation, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: function() {
- if ( mode === "hide" ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- }
- });
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect-transfer.js b/framework/yii/jui/assets/jquery.ui.effect-transfer.js
deleted file mode 100644
index 5543f76..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect-transfer.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/*!
- * jQuery UI Effects Transfer 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/transfer-effect/
- *
- * Depends:
- * jquery.ui.effect.js
- */
-(function( $, undefined ) {
-
-$.effects.effect.transfer = function( o, done ) {
- var elem = $( this ),
- target = $( o.to ),
- targetFixed = target.css( "position" ) === "fixed",
- body = $("body"),
- fixTop = targetFixed ? body.scrollTop() : 0,
- fixLeft = targetFixed ? body.scrollLeft() : 0,
- endPosition = target.offset(),
- animation = {
- top: endPosition.top - fixTop ,
- left: endPosition.left - fixLeft ,
- height: target.innerHeight(),
- width: target.innerWidth()
- },
- startPosition = elem.offset(),
- transfer = $( "
" )
- .appendTo( document.body )
- .addClass( o.className )
- .css({
- top: startPosition.top - fixTop ,
- left: startPosition.left - fixLeft ,
- height: elem.innerHeight(),
- width: elem.innerWidth(),
- position: targetFixed ? "fixed" : "absolute"
- })
- .animate( animation, o.duration, o.easing, function() {
- transfer.remove();
- done();
- });
-};
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/jquery.ui.effect.js b/framework/yii/jui/assets/jquery.ui.effect.js
deleted file mode 100644
index bad97fa..0000000
--- a/framework/yii/jui/assets/jquery.ui.effect.js
+++ /dev/null
@@ -1,1289 +0,0 @@
-/*!
- * jQuery UI Effects 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/category/effects-core/
- */
-(function($, undefined) {
-
-var dataSpace = "ui-effects-";
-
-$.effects = {
- effect: {}
-};
-
-/*!
- * jQuery Color Animations v2.1.2
- * https://github.com/jquery/jquery-color
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * Date: Wed Jan 16 08:47:09 2013 -0600
- */
-(function( jQuery, undefined ) {
-
- var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
-
- // plusequals test for += 100 -= 100
- rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
- // a set of RE's that can match strings and generate color tuples.
- stringParsers = [{
- re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
- parse: function( execResult ) {
- return [
- execResult[ 1 ],
- execResult[ 2 ],
- execResult[ 3 ],
- execResult[ 4 ]
- ];
- }
- }, {
- re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
- parse: function( execResult ) {
- return [
- execResult[ 1 ] * 2.55,
- execResult[ 2 ] * 2.55,
- execResult[ 3 ] * 2.55,
- execResult[ 4 ]
- ];
- }
- }, {
- // this regex ignores A-F because it's compared against an already lowercased string
- re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
- parse: function( execResult ) {
- return [
- parseInt( execResult[ 1 ], 16 ),
- parseInt( execResult[ 2 ], 16 ),
- parseInt( execResult[ 3 ], 16 )
- ];
- }
- }, {
- // this regex ignores A-F because it's compared against an already lowercased string
- re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
- parse: function( execResult ) {
- return [
- parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
- parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
- parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
- ];
- }
- }, {
- re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
- space: "hsla",
- parse: function( execResult ) {
- return [
- execResult[ 1 ],
- execResult[ 2 ] / 100,
- execResult[ 3 ] / 100,
- execResult[ 4 ]
- ];
- }
- }],
-
- // jQuery.Color( )
- color = jQuery.Color = function( color, green, blue, alpha ) {
- return new jQuery.Color.fn.parse( color, green, blue, alpha );
- },
- spaces = {
- rgba: {
- props: {
- red: {
- idx: 0,
- type: "byte"
- },
- green: {
- idx: 1,
- type: "byte"
- },
- blue: {
- idx: 2,
- type: "byte"
- }
- }
- },
-
- hsla: {
- props: {
- hue: {
- idx: 0,
- type: "degrees"
- },
- saturation: {
- idx: 1,
- type: "percent"
- },
- lightness: {
- idx: 2,
- type: "percent"
- }
- }
- }
- },
- propTypes = {
- "byte": {
- floor: true,
- max: 255
- },
- "percent": {
- max: 1
- },
- "degrees": {
- mod: 360,
- floor: true
- }
- },
- support = color.support = {},
-
- // element for support tests
- supportElem = jQuery( "" )[ 0 ],
-
- // colors = jQuery.Color.names
- colors,
-
- // local aliases of functions called often
- each = jQuery.each;
-
-// determine rgba support immediately
-supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
-support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
-
-// define cache name and alpha properties
-// for rgba and hsla spaces
-each( spaces, function( spaceName, space ) {
- space.cache = "_" + spaceName;
- space.props.alpha = {
- idx: 3,
- type: "percent",
- def: 1
- };
-});
-
-function clamp( value, prop, allowEmpty ) {
- var type = propTypes[ prop.type ] || {};
-
- if ( value == null ) {
- return (allowEmpty || !prop.def) ? null : prop.def;
- }
-
- // ~~ is an short way of doing floor for positive numbers
- value = type.floor ? ~~value : parseFloat( value );
-
- // IE will pass in empty strings as value for alpha,
- // which will hit this case
- if ( isNaN( value ) ) {
- return prop.def;
- }
-
- if ( type.mod ) {
- // we add mod before modding to make sure that negatives values
- // get converted properly: -10 -> 350
- return (value + type.mod) % type.mod;
- }
-
- // for now all property types without mod have min and max
- return 0 > value ? 0 : type.max < value ? type.max : value;
-}
-
-function stringParse( string ) {
- var inst = color(),
- rgba = inst._rgba = [];
-
- string = string.toLowerCase();
-
- each( stringParsers, function( i, parser ) {
- var parsed,
- match = parser.re.exec( string ),
- values = match && parser.parse( match ),
- spaceName = parser.space || "rgba";
-
- if ( values ) {
- parsed = inst[ spaceName ]( values );
-
- // if this was an rgba parse the assignment might happen twice
- // oh well....
- inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
- rgba = inst._rgba = parsed._rgba;
-
- // exit each( stringParsers ) here because we matched
- return false;
- }
- });
-
- // Found a stringParser that handled it
- if ( rgba.length ) {
-
- // if this came from a parsed string, force "transparent" when alpha is 0
- // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
- if ( rgba.join() === "0,0,0,0" ) {
- jQuery.extend( rgba, colors.transparent );
- }
- return inst;
- }
-
- // named colors
- return colors[ string ];
-}
-
-color.fn = jQuery.extend( color.prototype, {
- parse: function( red, green, blue, alpha ) {
- if ( red === undefined ) {
- this._rgba = [ null, null, null, null ];
- return this;
- }
- if ( red.jquery || red.nodeType ) {
- red = jQuery( red ).css( green );
- green = undefined;
- }
-
- var inst = this,
- type = jQuery.type( red ),
- rgba = this._rgba = [];
-
- // more than 1 argument specified - assume ( red, green, blue, alpha )
- if ( green !== undefined ) {
- red = [ red, green, blue, alpha ];
- type = "array";
- }
-
- if ( type === "string" ) {
- return this.parse( stringParse( red ) || colors._default );
- }
-
- if ( type === "array" ) {
- each( spaces.rgba.props, function( key, prop ) {
- rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
- });
- return this;
- }
-
- if ( type === "object" ) {
- if ( red instanceof color ) {
- each( spaces, function( spaceName, space ) {
- if ( red[ space.cache ] ) {
- inst[ space.cache ] = red[ space.cache ].slice();
- }
- });
- } else {
- each( spaces, function( spaceName, space ) {
- var cache = space.cache;
- each( space.props, function( key, prop ) {
-
- // if the cache doesn't exist, and we know how to convert
- if ( !inst[ cache ] && space.to ) {
-
- // if the value was null, we don't need to copy it
- // if the key was alpha, we don't need to copy it either
- if ( key === "alpha" || red[ key ] == null ) {
- return;
- }
- inst[ cache ] = space.to( inst._rgba );
- }
-
- // this is the only case where we allow nulls for ALL properties.
- // call clamp with alwaysAllowEmpty
- inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
- });
-
- // everything defined but alpha?
- if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
- // use the default of 1
- inst[ cache ][ 3 ] = 1;
- if ( space.from ) {
- inst._rgba = space.from( inst[ cache ] );
- }
- }
- });
- }
- return this;
- }
- },
- is: function( compare ) {
- var is = color( compare ),
- same = true,
- inst = this;
-
- each( spaces, function( _, space ) {
- var localCache,
- isCache = is[ space.cache ];
- if (isCache) {
- localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
- each( space.props, function( _, prop ) {
- if ( isCache[ prop.idx ] != null ) {
- same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
- return same;
- }
- });
- }
- return same;
- });
- return same;
- },
- _space: function() {
- var used = [],
- inst = this;
- each( spaces, function( spaceName, space ) {
- if ( inst[ space.cache ] ) {
- used.push( spaceName );
- }
- });
- return used.pop();
- },
- transition: function( other, distance ) {
- var end = color( other ),
- spaceName = end._space(),
- space = spaces[ spaceName ],
- startColor = this.alpha() === 0 ? color( "transparent" ) : this,
- start = startColor[ space.cache ] || space.to( startColor._rgba ),
- result = start.slice();
-
- end = end[ space.cache ];
- each( space.props, function( key, prop ) {
- var index = prop.idx,
- startValue = start[ index ],
- endValue = end[ index ],
- type = propTypes[ prop.type ] || {};
-
- // if null, don't override start value
- if ( endValue === null ) {
- return;
- }
- // if null - use end
- if ( startValue === null ) {
- result[ index ] = endValue;
- } else {
- if ( type.mod ) {
- if ( endValue - startValue > type.mod / 2 ) {
- startValue += type.mod;
- } else if ( startValue - endValue > type.mod / 2 ) {
- startValue -= type.mod;
- }
- }
- result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
- }
- });
- return this[ spaceName ]( result );
- },
- blend: function( opaque ) {
- // if we are already opaque - return ourself
- if ( this._rgba[ 3 ] === 1 ) {
- return this;
- }
-
- var rgb = this._rgba.slice(),
- a = rgb.pop(),
- blend = color( opaque )._rgba;
-
- return color( jQuery.map( rgb, function( v, i ) {
- return ( 1 - a ) * blend[ i ] + a * v;
- }));
- },
- toRgbaString: function() {
- var prefix = "rgba(",
- rgba = jQuery.map( this._rgba, function( v, i ) {
- return v == null ? ( i > 2 ? 1 : 0 ) : v;
- });
-
- if ( rgba[ 3 ] === 1 ) {
- rgba.pop();
- prefix = "rgb(";
- }
-
- return prefix + rgba.join() + ")";
- },
- toHslaString: function() {
- var prefix = "hsla(",
- hsla = jQuery.map( this.hsla(), function( v, i ) {
- if ( v == null ) {
- v = i > 2 ? 1 : 0;
- }
-
- // catch 1 and 2
- if ( i && i < 3 ) {
- v = Math.round( v * 100 ) + "%";
- }
- return v;
- });
-
- if ( hsla[ 3 ] === 1 ) {
- hsla.pop();
- prefix = "hsl(";
- }
- return prefix + hsla.join() + ")";
- },
- toHexString: function( includeAlpha ) {
- var rgba = this._rgba.slice(),
- alpha = rgba.pop();
-
- if ( includeAlpha ) {
- rgba.push( ~~( alpha * 255 ) );
- }
-
- return "#" + jQuery.map( rgba, function( v ) {
-
- // default to 0 when nulls exist
- v = ( v || 0 ).toString( 16 );
- return v.length === 1 ? "0" + v : v;
- }).join("");
- },
- toString: function() {
- return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
- }
-});
-color.fn.parse.prototype = color.fn;
-
-// hsla conversions adapted from:
-// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
-
-function hue2rgb( p, q, h ) {
- h = ( h + 1 ) % 1;
- if ( h * 6 < 1 ) {
- return p + (q - p) * h * 6;
- }
- if ( h * 2 < 1) {
- return q;
- }
- if ( h * 3 < 2 ) {
- return p + (q - p) * ((2/3) - h) * 6;
- }
- return p;
-}
-
-spaces.hsla.to = function ( rgba ) {
- if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
- return [ null, null, null, rgba[ 3 ] ];
- }
- var r = rgba[ 0 ] / 255,
- g = rgba[ 1 ] / 255,
- b = rgba[ 2 ] / 255,
- a = rgba[ 3 ],
- max = Math.max( r, g, b ),
- min = Math.min( r, g, b ),
- diff = max - min,
- add = max + min,
- l = add * 0.5,
- h, s;
-
- if ( min === max ) {
- h = 0;
- } else if ( r === max ) {
- h = ( 60 * ( g - b ) / diff ) + 360;
- } else if ( g === max ) {
- h = ( 60 * ( b - r ) / diff ) + 120;
- } else {
- h = ( 60 * ( r - g ) / diff ) + 240;
- }
-
- // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
- // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
- if ( diff === 0 ) {
- s = 0;
- } else if ( l <= 0.5 ) {
- s = diff / add;
- } else {
- s = diff / ( 2 - add );
- }
- return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
-};
-
-spaces.hsla.from = function ( hsla ) {
- if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
- return [ null, null, null, hsla[ 3 ] ];
- }
- var h = hsla[ 0 ] / 360,
- s = hsla[ 1 ],
- l = hsla[ 2 ],
- a = hsla[ 3 ],
- q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
- p = 2 * l - q;
-
- return [
- Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
- Math.round( hue2rgb( p, q, h ) * 255 ),
- Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
- a
- ];
-};
-
-
-each( spaces, function( spaceName, space ) {
- var props = space.props,
- cache = space.cache,
- to = space.to,
- from = space.from;
-
- // makes rgba() and hsla()
- color.fn[ spaceName ] = function( value ) {
-
- // generate a cache for this space if it doesn't exist
- if ( to && !this[ cache ] ) {
- this[ cache ] = to( this._rgba );
- }
- if ( value === undefined ) {
- return this[ cache ].slice();
- }
-
- var ret,
- type = jQuery.type( value ),
- arr = ( type === "array" || type === "object" ) ? value : arguments,
- local = this[ cache ].slice();
-
- each( props, function( key, prop ) {
- var val = arr[ type === "object" ? key : prop.idx ];
- if ( val == null ) {
- val = local[ prop.idx ];
- }
- local[ prop.idx ] = clamp( val, prop );
- });
-
- if ( from ) {
- ret = color( from( local ) );
- ret[ cache ] = local;
- return ret;
- } else {
- return color( local );
- }
- };
-
- // makes red() green() blue() alpha() hue() saturation() lightness()
- each( props, function( key, prop ) {
- // alpha is included in more than one space
- if ( color.fn[ key ] ) {
- return;
- }
- color.fn[ key ] = function( value ) {
- var vtype = jQuery.type( value ),
- fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
- local = this[ fn ](),
- cur = local[ prop.idx ],
- match;
-
- if ( vtype === "undefined" ) {
- return cur;
- }
-
- if ( vtype === "function" ) {
- value = value.call( this, cur );
- vtype = jQuery.type( value );
- }
- if ( value == null && prop.empty ) {
- return this;
- }
- if ( vtype === "string" ) {
- match = rplusequals.exec( value );
- if ( match ) {
- value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
- }
- }
- local[ prop.idx ] = value;
- return this[ fn ]( local );
- };
- });
-});
-
-// add cssHook and .fx.step function for each named hook.
-// accept a space separated string of properties
-color.hook = function( hook ) {
- var hooks = hook.split( " " );
- each( hooks, function( i, hook ) {
- jQuery.cssHooks[ hook ] = {
- set: function( elem, value ) {
- var parsed, curElem,
- backgroundColor = "";
-
- if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
- value = color( parsed || value );
- if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
- curElem = hook === "backgroundColor" ? elem.parentNode : elem;
- while (
- (backgroundColor === "" || backgroundColor === "transparent") &&
- curElem && curElem.style
- ) {
- try {
- backgroundColor = jQuery.css( curElem, "backgroundColor" );
- curElem = curElem.parentNode;
- } catch ( e ) {
- }
- }
-
- value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
- backgroundColor :
- "_default" );
- }
-
- value = value.toRgbaString();
- }
- try {
- elem.style[ hook ] = value;
- } catch( e ) {
- // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
- }
- }
- };
- jQuery.fx.step[ hook ] = function( fx ) {
- if ( !fx.colorInit ) {
- fx.start = color( fx.elem, hook );
- fx.end = color( fx.end );
- fx.colorInit = true;
- }
- jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
- };
- });
-
-};
-
-color.hook( stepHooks );
-
-jQuery.cssHooks.borderColor = {
- expand: function( value ) {
- var expanded = {};
-
- each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
- expanded[ "border" + part + "Color" ] = value;
- });
- return expanded;
- }
-};
-
-// Basic color names only.
-// Usage of any of the other color names requires adding yourself or including
-// jquery.color.svg-names.js.
-colors = jQuery.Color.names = {
- // 4.1. Basic color keywords
- aqua: "#00ffff",
- black: "#000000",
- blue: "#0000ff",
- fuchsia: "#ff00ff",
- gray: "#808080",
- green: "#008000",
- lime: "#00ff00",
- maroon: "#800000",
- navy: "#000080",
- olive: "#808000",
- purple: "#800080",
- red: "#ff0000",
- silver: "#c0c0c0",
- teal: "#008080",
- white: "#ffffff",
- yellow: "#ffff00",
-
- // 4.2.3. "transparent" color keyword
- transparent: [ null, null, null, 0 ],
-
- _default: "#ffffff"
-};
-
-})( jQuery );
-
-
-/******************************************************************************/
-/****************************** CLASS ANIMATIONS ******************************/
-/******************************************************************************/
-(function() {
-
-var classAnimationActions = [ "add", "remove", "toggle" ],
- shorthandStyles = {
- border: 1,
- borderBottom: 1,
- borderColor: 1,
- borderLeft: 1,
- borderRight: 1,
- borderTop: 1,
- borderWidth: 1,
- margin: 1,
- padding: 1
- };
-
-$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
- $.fx.step[ prop ] = function( fx ) {
- if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
- jQuery.style( fx.elem, prop, fx.end );
- fx.setAttr = true;
- }
- };
-});
-
-function getElementStyles( elem ) {
- var key, len,
- style = elem.ownerDocument.defaultView ?
- elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
- elem.currentStyle,
- styles = {};
-
- if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
- len = style.length;
- while ( len-- ) {
- key = style[ len ];
- if ( typeof style[ key ] === "string" ) {
- styles[ $.camelCase( key ) ] = style[ key ];
- }
- }
- // support: Opera, IE <9
- } else {
- for ( key in style ) {
- if ( typeof style[ key ] === "string" ) {
- styles[ key ] = style[ key ];
- }
- }
- }
-
- return styles;
-}
-
-
-function styleDifference( oldStyle, newStyle ) {
- var diff = {},
- name, value;
-
- for ( name in newStyle ) {
- value = newStyle[ name ];
- if ( oldStyle[ name ] !== value ) {
- if ( !shorthandStyles[ name ] ) {
- if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
- diff[ name ] = value;
- }
- }
- }
- }
-
- return diff;
-}
-
-// support: jQuery <1.8
-if ( !$.fn.addBack ) {
- $.fn.addBack = function( selector ) {
- return this.add( selector == null ?
- this.prevObject : this.prevObject.filter( selector )
- );
- };
-}
-
-$.effects.animateClass = function( value, duration, easing, callback ) {
- var o = $.speed( duration, easing, callback );
-
- return this.queue( function() {
- var animated = $( this ),
- baseClass = animated.attr( "class" ) || "",
- applyClassChange,
- allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
-
- // map the animated objects to store the original styles.
- allAnimations = allAnimations.map(function() {
- var el = $( this );
- return {
- el: el,
- start: getElementStyles( this )
- };
- });
-
- // apply class change
- applyClassChange = function() {
- $.each( classAnimationActions, function(i, action) {
- if ( value[ action ] ) {
- animated[ action + "Class" ]( value[ action ] );
- }
- });
- };
- applyClassChange();
-
- // map all animated objects again - calculate new styles and diff
- allAnimations = allAnimations.map(function() {
- this.end = getElementStyles( this.el[ 0 ] );
- this.diff = styleDifference( this.start, this.end );
- return this;
- });
-
- // apply original class
- animated.attr( "class", baseClass );
-
- // map all animated objects again - this time collecting a promise
- allAnimations = allAnimations.map(function() {
- var styleInfo = this,
- dfd = $.Deferred(),
- opts = $.extend({}, o, {
- queue: false,
- complete: function() {
- dfd.resolve( styleInfo );
- }
- });
-
- this.el.animate( this.diff, opts );
- return dfd.promise();
- });
-
- // once all animations have completed:
- $.when.apply( $, allAnimations.get() ).done(function() {
-
- // set the final class
- applyClassChange();
-
- // for each animated element,
- // clear all css properties that were animated
- $.each( arguments, function() {
- var el = this.el;
- $.each( this.diff, function(key) {
- el.css( key, "" );
- });
- });
-
- // this is guarnteed to be there if you use jQuery.speed()
- // it also handles dequeuing the next anim...
- o.complete.call( animated[ 0 ] );
- });
- });
-};
-
-$.fn.extend({
- addClass: (function( orig ) {
- return function( classNames, speed, easing, callback ) {
- return speed ?
- $.effects.animateClass.call( this,
- { add: classNames }, speed, easing, callback ) :
- orig.apply( this, arguments );
- };
- })( $.fn.addClass ),
-
- removeClass: (function( orig ) {
- return function( classNames, speed, easing, callback ) {
- return arguments.length > 1 ?
- $.effects.animateClass.call( this,
- { remove: classNames }, speed, easing, callback ) :
- orig.apply( this, arguments );
- };
- })( $.fn.removeClass ),
-
- toggleClass: (function( orig ) {
- return function( classNames, force, speed, easing, callback ) {
- if ( typeof force === "boolean" || force === undefined ) {
- if ( !speed ) {
- // without speed parameter
- return orig.apply( this, arguments );
- } else {
- return $.effects.animateClass.call( this,
- (force ? { add: classNames } : { remove: classNames }),
- speed, easing, callback );
- }
- } else {
- // without force parameter
- return $.effects.animateClass.call( this,
- { toggle: classNames }, force, speed, easing );
- }
- };
- })( $.fn.toggleClass ),
-
- switchClass: function( remove, add, speed, easing, callback) {
- return $.effects.animateClass.call( this, {
- add: add,
- remove: remove
- }, speed, easing, callback );
- }
-});
-
-})();
-
-/******************************************************************************/
-/*********************************** EFFECTS **********************************/
-/******************************************************************************/
-
-(function() {
-
-$.extend( $.effects, {
- version: "1.10.3",
-
- // Saves a set of properties in a data storage
- save: function( element, set ) {
- for( var i=0; i < set.length; i++ ) {
- if ( set[ i ] !== null ) {
- element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
- }
- }
- },
-
- // Restores a set of previously saved properties from a data storage
- restore: function( element, set ) {
- var val, i;
- for( i=0; i < set.length; i++ ) {
- if ( set[ i ] !== null ) {
- val = element.data( dataSpace + set[ i ] );
- // support: jQuery 1.6.2
- // http://bugs.jquery.com/ticket/9917
- // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
- // We can't differentiate between "" and 0 here, so we just assume
- // empty string since it's likely to be a more common value...
- if ( val === undefined ) {
- val = "";
- }
- element.css( set[ i ], val );
- }
- }
- },
-
- setMode: function( el, mode ) {
- if (mode === "toggle") {
- mode = el.is( ":hidden" ) ? "show" : "hide";
- }
- return mode;
- },
-
- // Translates a [top,left] array into a baseline value
- // this should be a little more flexible in the future to handle a string & hash
- getBaseline: function( origin, original ) {
- var y, x;
- switch ( origin[ 0 ] ) {
- case "top": y = 0; break;
- case "middle": y = 0.5; break;
- case "bottom": y = 1; break;
- default: y = origin[ 0 ] / original.height;
- }
- switch ( origin[ 1 ] ) {
- case "left": x = 0; break;
- case "center": x = 0.5; break;
- case "right": x = 1; break;
- default: x = origin[ 1 ] / original.width;
- }
- return {
- x: x,
- y: y
- };
- },
-
- // Wraps the element around a wrapper that copies position properties
- createWrapper: function( element ) {
-
- // if the element is already wrapped, return it
- if ( element.parent().is( ".ui-effects-wrapper" )) {
- return element.parent();
- }
-
- // wrap the element
- var props = {
- width: element.outerWidth(true),
- height: element.outerHeight(true),
- "float": element.css( "float" )
- },
- wrapper = $( "
" )
- .addClass( "ui-effects-wrapper" )
- .css({
- fontSize: "100%",
- background: "transparent",
- border: "none",
- margin: 0,
- padding: 0
- }),
- // Store the size in case width/height are defined in % - Fixes #5245
- size = {
- width: element.width(),
- height: element.height()
- },
- active = document.activeElement;
-
- // support: Firefox
- // Firefox incorrectly exposes anonymous content
- // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
- try {
- active.id;
- } catch( e ) {
- active = document.body;
- }
-
- element.wrap( wrapper );
-
- // Fixes #7595 - Elements lose focus when wrapped.
- if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
- $( active ).focus();
- }
-
- wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
-
- // transfer positioning properties to the wrapper
- if ( element.css( "position" ) === "static" ) {
- wrapper.css({ position: "relative" });
- element.css({ position: "relative" });
- } else {
- $.extend( props, {
- position: element.css( "position" ),
- zIndex: element.css( "z-index" )
- });
- $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
- props[ pos ] = element.css( pos );
- if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
- props[ pos ] = "auto";
- }
- });
- element.css({
- position: "relative",
- top: 0,
- left: 0,
- right: "auto",
- bottom: "auto"
- });
- }
- element.css(size);
-
- return wrapper.css( props ).show();
- },
-
- removeWrapper: function( element ) {
- var active = document.activeElement;
-
- if ( element.parent().is( ".ui-effects-wrapper" ) ) {
- element.parent().replaceWith( element );
-
- // Fixes #7595 - Elements lose focus when wrapped.
- if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
- $( active ).focus();
- }
- }
-
-
- return element;
- },
-
- setTransition: function( element, list, factor, value ) {
- value = value || {};
- $.each( list, function( i, x ) {
- var unit = element.cssUnit( x );
- if ( unit[ 0 ] > 0 ) {
- value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
- }
- });
- return value;
- }
-});
-
-// return an effect options object for the given parameters:
-function _normalizeArguments( effect, options, speed, callback ) {
-
- // allow passing all options as the first parameter
- if ( $.isPlainObject( effect ) ) {
- options = effect;
- effect = effect.effect;
- }
-
- // convert to an object
- effect = { effect: effect };
-
- // catch (effect, null, ...)
- if ( options == null ) {
- options = {};
- }
-
- // catch (effect, callback)
- if ( $.isFunction( options ) ) {
- callback = options;
- speed = null;
- options = {};
- }
-
- // catch (effect, speed, ?)
- if ( typeof options === "number" || $.fx.speeds[ options ] ) {
- callback = speed;
- speed = options;
- options = {};
- }
-
- // catch (effect, options, callback)
- if ( $.isFunction( speed ) ) {
- callback = speed;
- speed = null;
- }
-
- // add options to effect
- if ( options ) {
- $.extend( effect, options );
- }
-
- speed = speed || options.duration;
- effect.duration = $.fx.off ? 0 :
- typeof speed === "number" ? speed :
- speed in $.fx.speeds ? $.fx.speeds[ speed ] :
- $.fx.speeds._default;
-
- effect.complete = callback || options.complete;
-
- return effect;
-}
-
-function standardAnimationOption( option ) {
- // Valid standard speeds (nothing, number, named speed)
- if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
- return true;
- }
-
- // Invalid strings - treat as "normal" speed
- if ( typeof option === "string" && !$.effects.effect[ option ] ) {
- return true;
- }
-
- // Complete callback
- if ( $.isFunction( option ) ) {
- return true;
- }
-
- // Options hash (but not naming an effect)
- if ( typeof option === "object" && !option.effect ) {
- return true;
- }
-
- // Didn't match any standard API
- return false;
-}
-
-$.fn.extend({
- effect: function( /* effect, options, speed, callback */ ) {
- var args = _normalizeArguments.apply( this, arguments ),
- mode = args.mode,
- queue = args.queue,
- effectMethod = $.effects.effect[ args.effect ];
-
- if ( $.fx.off || !effectMethod ) {
- // delegate to the original method (e.g., .show()) if possible
- if ( mode ) {
- return this[ mode ]( args.duration, args.complete );
- } else {
- return this.each( function() {
- if ( args.complete ) {
- args.complete.call( this );
- }
- });
- }
- }
-
- function run( next ) {
- var elem = $( this ),
- complete = args.complete,
- mode = args.mode;
-
- function done() {
- if ( $.isFunction( complete ) ) {
- complete.call( elem[0] );
- }
- if ( $.isFunction( next ) ) {
- next();
- }
- }
-
- // If the element already has the correct final state, delegate to
- // the core methods so the internal tracking of "olddisplay" works.
- if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
- elem[ mode ]();
- done();
- } else {
- effectMethod.call( elem[0], args, done );
- }
- }
-
- return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
- },
-
- show: (function( orig ) {
- return function( option ) {
- if ( standardAnimationOption( option ) ) {
- return orig.apply( this, arguments );
- } else {
- var args = _normalizeArguments.apply( this, arguments );
- args.mode = "show";
- return this.effect.call( this, args );
- }
- };
- })( $.fn.show ),
-
- hide: (function( orig ) {
- return function( option ) {
- if ( standardAnimationOption( option ) ) {
- return orig.apply( this, arguments );
- } else {
- var args = _normalizeArguments.apply( this, arguments );
- args.mode = "hide";
- return this.effect.call( this, args );
- }
- };
- })( $.fn.hide ),
-
- toggle: (function( orig ) {
- return function( option ) {
- if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
- return orig.apply( this, arguments );
- } else {
- var args = _normalizeArguments.apply( this, arguments );
- args.mode = "toggle";
- return this.effect.call( this, args );
- }
- };
- })( $.fn.toggle ),
-
- // helper functions
- cssUnit: function(key) {
- var style = this.css( key ),
- val = [];
-
- $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
- if ( style.indexOf( unit ) > 0 ) {
- val = [ parseFloat( style ), unit ];
- }
- });
- return val;
- }
-});
-
-})();
-
-/******************************************************************************/
-/*********************************** EASING ***********************************/
-/******************************************************************************/
-
-(function() {
-
-// based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
-
-var baseEasings = {};
-
-$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
- baseEasings[ name ] = function( p ) {
- return Math.pow( p, i + 2 );
- };
-});
-
-$.extend( baseEasings, {
- Sine: function ( p ) {
- return 1 - Math.cos( p * Math.PI / 2 );
- },
- Circ: function ( p ) {
- return 1 - Math.sqrt( 1 - p * p );
- },
- Elastic: function( p ) {
- return p === 0 || p === 1 ? p :
- -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
- },
- Back: function( p ) {
- return p * p * ( 3 * p - 2 );
- },
- Bounce: function ( p ) {
- var pow2,
- bounce = 4;
-
- while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
- return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
- }
-});
-
-$.each( baseEasings, function( name, easeIn ) {
- $.easing[ "easeIn" + name ] = easeIn;
- $.easing[ "easeOut" + name ] = function( p ) {
- return 1 - easeIn( 1 - p );
- };
- $.easing[ "easeInOut" + name ] = function( p ) {
- return p < 0.5 ?
- easeIn( p * 2 ) / 2 :
- 1 - easeIn( p * -2 + 2 ) / 2;
- };
-});
-
-})();
-
-})(jQuery);
diff --git a/framework/yii/jui/assets/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png b/framework/yii/jui/assets/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png
deleted file mode 100644
index 5b5dab2..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-bg_flat_75_ffffff_40x100.png b/framework/yii/jui/assets/themes/base/images/ui-bg_flat_75_ffffff_40x100.png
deleted file mode 100644
index ac8b229..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-bg_flat_75_ffffff_40x100.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png b/framework/yii/jui/assets/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png
deleted file mode 100644
index ad3d634..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-bg_glass_65_ffffff_1x400.png b/framework/yii/jui/assets/themes/base/images/ui-bg_glass_65_ffffff_1x400.png
deleted file mode 100644
index 42ccba2..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-bg_glass_65_ffffff_1x400.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-bg_glass_75_dadada_1x400.png b/framework/yii/jui/assets/themes/base/images/ui-bg_glass_75_dadada_1x400.png
deleted file mode 100644
index 5a46b47..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-bg_glass_75_dadada_1x400.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png b/framework/yii/jui/assets/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png
deleted file mode 100644
index 86c2baa..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png b/framework/yii/jui/assets/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png
deleted file mode 100644
index 4443fdc..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/framework/yii/jui/assets/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png
deleted file mode 100644
index 7c9fa6c..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-icons_222222_256x240.png b/framework/yii/jui/assets/themes/base/images/ui-icons_222222_256x240.png
deleted file mode 100644
index ee039dc..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-icons_222222_256x240.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-icons_2e83ff_256x240.png b/framework/yii/jui/assets/themes/base/images/ui-icons_2e83ff_256x240.png
deleted file mode 100644
index 45e8928..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-icons_2e83ff_256x240.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-icons_454545_256x240.png b/framework/yii/jui/assets/themes/base/images/ui-icons_454545_256x240.png
deleted file mode 100644
index 7ec70d1..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-icons_454545_256x240.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-icons_888888_256x240.png b/framework/yii/jui/assets/themes/base/images/ui-icons_888888_256x240.png
deleted file mode 100644
index 5ba708c..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-icons_888888_256x240.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/images/ui-icons_cd0a0a_256x240.png b/framework/yii/jui/assets/themes/base/images/ui-icons_cd0a0a_256x240.png
deleted file mode 100644
index 7930a55..0000000
Binary files a/framework/yii/jui/assets/themes/base/images/ui-icons_cd0a0a_256x240.png and /dev/null differ
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.accordion.css b/framework/yii/jui/assets/themes/base/jquery.ui.accordion.css
deleted file mode 100644
index d36f910..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.accordion.css
+++ /dev/null
@@ -1,38 +0,0 @@
-/*!
- * jQuery UI Accordion 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Accordion#theming
- */
-.ui-accordion .ui-accordion-header {
- display: block;
- cursor: pointer;
- position: relative;
- margin-top: 2px;
- padding: .5em .5em .5em .7em;
- min-height: 0; /* support: IE7 */
-}
-.ui-accordion .ui-accordion-icons {
- padding-left: 2.2em;
-}
-.ui-accordion .ui-accordion-noicons {
- padding-left: .7em;
-}
-.ui-accordion .ui-accordion-icons .ui-accordion-icons {
- padding-left: 2.2em;
-}
-.ui-accordion .ui-accordion-header .ui-accordion-header-icon {
- position: absolute;
- left: .5em;
- top: 50%;
- margin-top: -8px;
-}
-.ui-accordion .ui-accordion-content {
- padding: 1em 2.2em;
- border-top: 0;
- overflow: auto;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.autocomplete.css b/framework/yii/jui/assets/themes/base/jquery.ui.autocomplete.css
deleted file mode 100644
index 5e2c750..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.autocomplete.css
+++ /dev/null
@@ -1,16 +0,0 @@
-/*!
- * jQuery UI Autocomplete 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Autocomplete#theming
- */
-.ui-autocomplete {
- position: absolute;
- top: 0;
- left: 0;
- cursor: default;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.button.css b/framework/yii/jui/assets/themes/base/jquery.ui.button.css
deleted file mode 100644
index 52d6c13..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.button.css
+++ /dev/null
@@ -1,114 +0,0 @@
-/*!
- * jQuery UI Button 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Button#theming
- */
-.ui-button {
- display: inline-block;
- position: relative;
- padding: 0;
- line-height: normal;
- margin-right: .1em;
- cursor: pointer;
- vertical-align: middle;
- text-align: center;
- overflow: visible; /* removes extra width in IE */
-}
-.ui-button,
-.ui-button:link,
-.ui-button:visited,
-.ui-button:hover,
-.ui-button:active {
- text-decoration: none;
-}
-/* to make room for the icon, a width needs to be set here */
-.ui-button-icon-only {
- width: 2.2em;
-}
-/* button elements seem to need a little more width */
-button.ui-button-icon-only {
- width: 2.4em;
-}
-.ui-button-icons-only {
- width: 3.4em;
-}
-button.ui-button-icons-only {
- width: 3.7em;
-}
-
-/* button text element */
-.ui-button .ui-button-text {
- display: block;
- line-height: normal;
-}
-.ui-button-text-only .ui-button-text {
- padding: .4em 1em;
-}
-.ui-button-icon-only .ui-button-text,
-.ui-button-icons-only .ui-button-text {
- padding: .4em;
- text-indent: -9999999px;
-}
-.ui-button-text-icon-primary .ui-button-text,
-.ui-button-text-icons .ui-button-text {
- padding: .4em 1em .4em 2.1em;
-}
-.ui-button-text-icon-secondary .ui-button-text,
-.ui-button-text-icons .ui-button-text {
- padding: .4em 2.1em .4em 1em;
-}
-.ui-button-text-icons .ui-button-text {
- padding-left: 2.1em;
- padding-right: 2.1em;
-}
-/* no icon support for input elements, provide padding by default */
-input.ui-button {
- padding: .4em 1em;
-}
-
-/* button icon element(s) */
-.ui-button-icon-only .ui-icon,
-.ui-button-text-icon-primary .ui-icon,
-.ui-button-text-icon-secondary .ui-icon,
-.ui-button-text-icons .ui-icon,
-.ui-button-icons-only .ui-icon {
- position: absolute;
- top: 50%;
- margin-top: -8px;
-}
-.ui-button-icon-only .ui-icon {
- left: 50%;
- margin-left: -8px;
-}
-.ui-button-text-icon-primary .ui-button-icon-primary,
-.ui-button-text-icons .ui-button-icon-primary,
-.ui-button-icons-only .ui-button-icon-primary {
- left: .5em;
-}
-.ui-button-text-icon-secondary .ui-button-icon-secondary,
-.ui-button-text-icons .ui-button-icon-secondary,
-.ui-button-icons-only .ui-button-icon-secondary {
- right: .5em;
-}
-
-/* button sets */
-.ui-buttonset {
- margin-right: 7px;
-}
-.ui-buttonset .ui-button {
- margin-left: 0;
- margin-right: -.3em;
-}
-
-/* workarounds */
-/* reset extra padding in Firefox, see h5bp.com/l */
-input.ui-button::-moz-focus-inner,
-button.ui-button::-moz-focus-inner {
- border: 0;
- padding: 0;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.core.css b/framework/yii/jui/assets/themes/base/jquery.ui.core.css
deleted file mode 100644
index 04d6052..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.core.css
+++ /dev/null
@@ -1,93 +0,0 @@
-/*!
- * jQuery UI CSS Framework 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Theming/API
- */
-
-/* Layout helpers
-----------------------------------*/
-.ui-helper-hidden {
- display: none;
-}
-.ui-helper-hidden-accessible {
- border: 0;
- clip: rect(0 0 0 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- width: 1px;
-}
-.ui-helper-reset {
- margin: 0;
- padding: 0;
- border: 0;
- outline: 0;
- line-height: 1.3;
- text-decoration: none;
- font-size: 100%;
- list-style: none;
-}
-.ui-helper-clearfix:before,
-.ui-helper-clearfix:after {
- content: "";
- display: table;
- border-collapse: collapse;
-}
-.ui-helper-clearfix:after {
- clear: both;
-}
-.ui-helper-clearfix {
- min-height: 0; /* support: IE7 */
-}
-.ui-helper-zfix {
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- position: absolute;
- opacity: 0;
- filter:Alpha(Opacity=0);
-}
-
-.ui-front {
- z-index: 100;
-}
-
-
-/* Interaction Cues
-----------------------------------*/
-.ui-state-disabled {
- cursor: default !important;
-}
-
-
-/* Icons
-----------------------------------*/
-
-/* states and images */
-.ui-icon {
- display: block;
- text-indent: -99999px;
- overflow: hidden;
- background-repeat: no-repeat;
-}
-
-
-/* Misc visuals
-----------------------------------*/
-
-/* Overlays */
-.ui-widget-overlay {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.datepicker.css b/framework/yii/jui/assets/themes/base/jquery.ui.datepicker.css
deleted file mode 100644
index 58bc5d6..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.datepicker.css
+++ /dev/null
@@ -1,178 +0,0 @@
-/*!
- * jQuery UI Datepicker 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Datepicker#theming
- */
-.ui-datepicker {
- width: 17em;
- padding: .2em .2em 0;
- display: none;
-}
-.ui-datepicker .ui-datepicker-header {
- position: relative;
- padding: .2em 0;
-}
-.ui-datepicker .ui-datepicker-prev,
-.ui-datepicker .ui-datepicker-next {
- position: absolute;
- top: 2px;
- width: 1.8em;
- height: 1.8em;
-}
-.ui-datepicker .ui-datepicker-prev-hover,
-.ui-datepicker .ui-datepicker-next-hover {
- top: 1px;
-}
-.ui-datepicker .ui-datepicker-prev {
- left: 2px;
-}
-.ui-datepicker .ui-datepicker-next {
- right: 2px;
-}
-.ui-datepicker .ui-datepicker-prev-hover {
- left: 1px;
-}
-.ui-datepicker .ui-datepicker-next-hover {
- right: 1px;
-}
-.ui-datepicker .ui-datepicker-prev span,
-.ui-datepicker .ui-datepicker-next span {
- display: block;
- position: absolute;
- left: 50%;
- margin-left: -8px;
- top: 50%;
- margin-top: -8px;
-}
-.ui-datepicker .ui-datepicker-title {
- margin: 0 2.3em;
- line-height: 1.8em;
- text-align: center;
-}
-.ui-datepicker .ui-datepicker-title select {
- font-size: 1em;
- margin: 1px 0;
-}
-.ui-datepicker select.ui-datepicker-month-year {
- width: 100%;
-}
-.ui-datepicker select.ui-datepicker-month,
-.ui-datepicker select.ui-datepicker-year {
- width: 49%;
-}
-.ui-datepicker table {
- width: 100%;
- font-size: .9em;
- border-collapse: collapse;
- margin: 0 0 .4em;
-}
-.ui-datepicker th {
- padding: .7em .3em;
- text-align: center;
- font-weight: bold;
- border: 0;
-}
-.ui-datepicker td {
- border: 0;
- padding: 1px;
-}
-.ui-datepicker td span,
-.ui-datepicker td a {
- display: block;
- padding: .2em;
- text-align: right;
- text-decoration: none;
-}
-.ui-datepicker .ui-datepicker-buttonpane {
- background-image: none;
- margin: .7em 0 0 0;
- padding: 0 .2em;
- border-left: 0;
- border-right: 0;
- border-bottom: 0;
-}
-.ui-datepicker .ui-datepicker-buttonpane button {
- float: right;
- margin: .5em .2em .4em;
- cursor: pointer;
- padding: .2em .6em .3em .6em;
- width: auto;
- overflow: visible;
-}
-.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
- float: left;
-}
-
-/* with multiple calendars */
-.ui-datepicker.ui-datepicker-multi {
- width: auto;
-}
-.ui-datepicker-multi .ui-datepicker-group {
- float: left;
-}
-.ui-datepicker-multi .ui-datepicker-group table {
- width: 95%;
- margin: 0 auto .4em;
-}
-.ui-datepicker-multi-2 .ui-datepicker-group {
- width: 50%;
-}
-.ui-datepicker-multi-3 .ui-datepicker-group {
- width: 33.3%;
-}
-.ui-datepicker-multi-4 .ui-datepicker-group {
- width: 25%;
-}
-.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
-.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
- border-left-width: 0;
-}
-.ui-datepicker-multi .ui-datepicker-buttonpane {
- clear: left;
-}
-.ui-datepicker-row-break {
- clear: both;
- width: 100%;
- font-size: 0;
-}
-
-/* RTL support */
-.ui-datepicker-rtl {
- direction: rtl;
-}
-.ui-datepicker-rtl .ui-datepicker-prev {
- right: 2px;
- left: auto;
-}
-.ui-datepicker-rtl .ui-datepicker-next {
- left: 2px;
- right: auto;
-}
-.ui-datepicker-rtl .ui-datepicker-prev:hover {
- right: 1px;
- left: auto;
-}
-.ui-datepicker-rtl .ui-datepicker-next:hover {
- left: 1px;
- right: auto;
-}
-.ui-datepicker-rtl .ui-datepicker-buttonpane {
- clear: right;
-}
-.ui-datepicker-rtl .ui-datepicker-buttonpane button {
- float: left;
-}
-.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
-.ui-datepicker-rtl .ui-datepicker-group {
- float: right;
-}
-.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
-.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
- border-right-width: 0;
- border-left-width: 1px;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.dialog.css b/framework/yii/jui/assets/themes/base/jquery.ui.dialog.css
deleted file mode 100644
index 533d606..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.dialog.css
+++ /dev/null
@@ -1,69 +0,0 @@
-/*!
- * jQuery UI Dialog 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Dialog#theming
- */
-.ui-dialog {
- position: absolute;
- top: 0;
- left: 0;
- padding: .2em;
- outline: 0;
-}
-.ui-dialog .ui-dialog-titlebar {
- padding: .4em 1em;
- position: relative;
-}
-.ui-dialog .ui-dialog-title {
- float: left;
- margin: .1em 0;
- white-space: nowrap;
- width: 90%;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-.ui-dialog .ui-dialog-titlebar-close {
- position: absolute;
- right: .3em;
- top: 50%;
- width: 21px;
- margin: -10px 0 0 0;
- padding: 1px;
- height: 20px;
-}
-.ui-dialog .ui-dialog-content {
- position: relative;
- border: 0;
- padding: .5em 1em;
- background: none;
- overflow: auto;
-}
-.ui-dialog .ui-dialog-buttonpane {
- text-align: left;
- border-width: 1px 0 0 0;
- background-image: none;
- margin-top: .5em;
- padding: .3em 1em .5em .4em;
-}
-.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
- float: right;
-}
-.ui-dialog .ui-dialog-buttonpane button {
- margin: .5em .4em .5em 0;
- cursor: pointer;
-}
-.ui-dialog .ui-resizable-se {
- width: 12px;
- height: 12px;
- right: -5px;
- bottom: -5px;
- background-position: 16px 16px;
-}
-.ui-draggable .ui-dialog-titlebar {
- cursor: move;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.menu.css b/framework/yii/jui/assets/themes/base/jquery.ui.menu.css
deleted file mode 100644
index c48ab33..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.menu.css
+++ /dev/null
@@ -1,79 +0,0 @@
-/*!
- * jQuery UI Menu 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Menu#theming
- */
-.ui-menu {
- list-style: none;
- padding: 2px;
- margin: 0;
- display: block;
- outline: none;
-}
-.ui-menu .ui-menu {
- margin-top: -3px;
- position: absolute;
-}
-.ui-menu .ui-menu-item {
- margin: 0;
- padding: 0;
- width: 100%;
- /* support: IE10, see #8844 */
- list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
-}
-.ui-menu .ui-menu-divider {
- margin: 5px -2px 5px -2px;
- height: 0;
- font-size: 0;
- line-height: 0;
- border-width: 1px 0 0 0;
-}
-.ui-menu .ui-menu-item a {
- text-decoration: none;
- display: block;
- padding: 2px .4em;
- line-height: 1.5;
- min-height: 0; /* support: IE7 */
- font-weight: normal;
-}
-.ui-menu .ui-menu-item a.ui-state-focus,
-.ui-menu .ui-menu-item a.ui-state-active {
- font-weight: normal;
- margin: -1px;
-}
-
-.ui-menu .ui-state-disabled {
- font-weight: normal;
- margin: .4em 0 .2em;
- line-height: 1.5;
-}
-.ui-menu .ui-state-disabled a {
- cursor: default;
-}
-
-/* icon support */
-.ui-menu-icons {
- position: relative;
-}
-.ui-menu-icons .ui-menu-item a {
- position: relative;
- padding-left: 2em;
-}
-
-/* left-aligned */
-.ui-menu .ui-icon {
- position: absolute;
- top: .2em;
- left: .2em;
-}
-
-/* right-aligned */
-.ui-menu .ui-menu-icon {
- position: static;
- float: right;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.progressbar.css b/framework/yii/jui/assets/themes/base/jquery.ui.progressbar.css
deleted file mode 100644
index 958e231..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.progressbar.css
+++ /dev/null
@@ -1,28 +0,0 @@
-/*!
- * jQuery UI Progressbar 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Progressbar#theming
- */
-.ui-progressbar {
- height: 2em;
- text-align: left;
- overflow: hidden;
-}
-.ui-progressbar .ui-progressbar-value {
- margin: -1px;
- height: 100%;
-}
-.ui-progressbar .ui-progressbar-overlay {
- background: url("images/animated-overlay.gif");
- height: 100%;
- filter: alpha(opacity=25);
- opacity: 0.25;
-}
-.ui-progressbar-indeterminate .ui-progressbar-value {
- background-image: none;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.resizable.css b/framework/yii/jui/assets/themes/base/jquery.ui.resizable.css
deleted file mode 100644
index c46e935..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.resizable.css
+++ /dev/null
@@ -1,78 +0,0 @@
-/*!
- * jQuery UI Resizable 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Resizable#theming
- */
-.ui-resizable {
- position: relative;
-}
-.ui-resizable-handle {
- position: absolute;
- font-size: 0.1px;
- display: block;
-}
-.ui-resizable-disabled .ui-resizable-handle,
-.ui-resizable-autohide .ui-resizable-handle {
- display: none;
-}
-.ui-resizable-n {
- cursor: n-resize;
- height: 7px;
- width: 100%;
- top: -5px;
- left: 0;
-}
-.ui-resizable-s {
- cursor: s-resize;
- height: 7px;
- width: 100%;
- bottom: -5px;
- left: 0;
-}
-.ui-resizable-e {
- cursor: e-resize;
- width: 7px;
- right: -5px;
- top: 0;
- height: 100%;
-}
-.ui-resizable-w {
- cursor: w-resize;
- width: 7px;
- left: -5px;
- top: 0;
- height: 100%;
-}
-.ui-resizable-se {
- cursor: se-resize;
- width: 12px;
- height: 12px;
- right: 1px;
- bottom: 1px;
-}
-.ui-resizable-sw {
- cursor: sw-resize;
- width: 9px;
- height: 9px;
- left: -5px;
- bottom: -5px;
-}
-.ui-resizable-nw {
- cursor: nw-resize;
- width: 9px;
- height: 9px;
- left: -5px;
- top: -5px;
-}
-.ui-resizable-ne {
- cursor: ne-resize;
- width: 9px;
- height: 9px;
- right: -5px;
- top: -5px;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.selectable.css b/framework/yii/jui/assets/themes/base/jquery.ui.selectable.css
deleted file mode 100644
index 274cd44..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.selectable.css
+++ /dev/null
@@ -1,15 +0,0 @@
-/*!
- * jQuery UI Selectable 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Selectable#theming
- */
-.ui-selectable-helper {
- position: absolute;
- z-index: 100;
- border: 1px dotted black;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.slider.css b/framework/yii/jui/assets/themes/base/jquery.ui.slider.css
deleted file mode 100644
index f2d4952..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.slider.css
+++ /dev/null
@@ -1,73 +0,0 @@
-/*!
- * jQuery UI Slider 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Slider#theming
- */
-.ui-slider {
- position: relative;
- text-align: left;
-}
-.ui-slider .ui-slider-handle {
- position: absolute;
- z-index: 2;
- width: 1.2em;
- height: 1.2em;
- cursor: default;
-}
-.ui-slider .ui-slider-range {
- position: absolute;
- z-index: 1;
- font-size: .7em;
- display: block;
- border: 0;
- background-position: 0 0;
-}
-
-/* For IE8 - See #6727 */
-.ui-slider.ui-state-disabled .ui-slider-handle,
-.ui-slider.ui-state-disabled .ui-slider-range {
- filter: inherit;
-}
-
-.ui-slider-horizontal {
- height: .8em;
-}
-.ui-slider-horizontal .ui-slider-handle {
- top: -.3em;
- margin-left: -.6em;
-}
-.ui-slider-horizontal .ui-slider-range {
- top: 0;
- height: 100%;
-}
-.ui-slider-horizontal .ui-slider-range-min {
- left: 0;
-}
-.ui-slider-horizontal .ui-slider-range-max {
- right: 0;
-}
-
-.ui-slider-vertical {
- width: .8em;
- height: 100px;
-}
-.ui-slider-vertical .ui-slider-handle {
- left: -.3em;
- margin-left: 0;
- margin-bottom: -.6em;
-}
-.ui-slider-vertical .ui-slider-range {
- left: 0;
- width: 100%;
-}
-.ui-slider-vertical .ui-slider-range-min {
- bottom: 0;
-}
-.ui-slider-vertical .ui-slider-range-max {
- top: 0;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.spinner.css b/framework/yii/jui/assets/themes/base/jquery.ui.spinner.css
deleted file mode 100644
index 9a92c9f..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.spinner.css
+++ /dev/null
@@ -1,65 +0,0 @@
-/*!
- * jQuery UI Spinner 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Spinner#theming
- */
-.ui-spinner {
- position: relative;
- display: inline-block;
- overflow: hidden;
- padding: 0;
- vertical-align: middle;
-}
-.ui-spinner-input {
- border: none;
- background: none;
- color: inherit;
- padding: 0;
- margin: .2em 0;
- vertical-align: middle;
- margin-left: .4em;
- margin-right: 22px;
-}
-.ui-spinner-button {
- width: 16px;
- height: 50%;
- font-size: .5em;
- padding: 0;
- margin: 0;
- text-align: center;
- position: absolute;
- cursor: default;
- display: block;
- overflow: hidden;
- right: 0;
-}
-/* more specificity required here to overide default borders */
-.ui-spinner a.ui-spinner-button {
- border-top: none;
- border-bottom: none;
- border-right: none;
-}
-/* vertical centre icon */
-.ui-spinner .ui-icon {
- position: absolute;
- margin-top: -8px;
- top: 50%;
- left: 0;
-}
-.ui-spinner-up {
- top: 0;
-}
-.ui-spinner-down {
- bottom: 0;
-}
-
-/* TR overrides */
-.ui-spinner .ui-icon-triangle-1-s {
- /* need to fix icons sprite */
- background-position: -65px -16px;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.tabs.css b/framework/yii/jui/assets/themes/base/jquery.ui.tabs.css
deleted file mode 100644
index 26f9c27..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.tabs.css
+++ /dev/null
@@ -1,52 +0,0 @@
-/*!
- * jQuery UI Tabs 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Tabs#theming
- */
-.ui-tabs {
- position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
- padding: .2em;
-}
-.ui-tabs .ui-tabs-nav {
- margin: 0;
- padding: .2em .2em 0;
-}
-.ui-tabs .ui-tabs-nav li {
- list-style: none;
- float: left;
- position: relative;
- top: 0;
- margin: 1px .2em 0 0;
- border-bottom-width: 0;
- padding: 0;
- white-space: nowrap;
-}
-.ui-tabs .ui-tabs-nav li a {
- float: left;
- padding: .5em 1em;
- text-decoration: none;
-}
-.ui-tabs .ui-tabs-nav li.ui-tabs-active {
- margin-bottom: -1px;
- padding-bottom: 1px;
-}
-.ui-tabs .ui-tabs-nav li.ui-tabs-active a,
-.ui-tabs .ui-tabs-nav li.ui-state-disabled a,
-.ui-tabs .ui-tabs-nav li.ui-tabs-loading a {
- cursor: text;
-}
-.ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
-.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a {
- cursor: pointer;
-}
-.ui-tabs .ui-tabs-panel {
- display: block;
- border-width: 0;
- padding: 1em 1.4em;
- background: none;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.theme.css b/framework/yii/jui/assets/themes/base/jquery.ui.theme.css
deleted file mode 100644
index eef80c8..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.theme.css
+++ /dev/null
@@ -1,406 +0,0 @@
-/*!
- * jQuery UI CSS Framework 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Theming/API
- *
- * To view and modify this theme, visit http://jqueryui.com/themeroller/
- */
-
-
-/* Component containers
-----------------------------------*/
-.ui-widget {
- font-family: Verdana,Arial,sans-serif/*{ffDefault}*/;
- font-size: 1.1em/*{fsDefault}*/;
-}
-.ui-widget .ui-widget {
- font-size: 1em;
-}
-.ui-widget input,
-.ui-widget select,
-.ui-widget textarea,
-.ui-widget button {
- font-family: Verdana,Arial,sans-serif/*{ffDefault}*/;
- font-size: 1em;
-}
-.ui-widget-content {
- border: 1px solid #aaaaaa/*{borderColorContent}*/;
- background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/;
- color: #222222/*{fcContent}*/;
-}
-.ui-widget-content a {
- color: #222222/*{fcContent}*/;
-}
-.ui-widget-header {
- border: 1px solid #aaaaaa/*{borderColorHeader}*/;
- background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/;
- color: #222222/*{fcHeader}*/;
- font-weight: bold;
-}
-.ui-widget-header a {
- color: #222222/*{fcHeader}*/;
-}
-
-/* Interaction states
-----------------------------------*/
-.ui-state-default,
-.ui-widget-content .ui-state-default,
-.ui-widget-header .ui-state-default {
- border: 1px solid #d3d3d3/*{borderColorDefault}*/;
- background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/;
- font-weight: normal/*{fwDefault}*/;
- color: #555555/*{fcDefault}*/;
-}
-.ui-state-default a,
-.ui-state-default a:link,
-.ui-state-default a:visited {
- color: #555555/*{fcDefault}*/;
- text-decoration: none;
-}
-.ui-state-hover,
-.ui-widget-content .ui-state-hover,
-.ui-widget-header .ui-state-hover,
-.ui-state-focus,
-.ui-widget-content .ui-state-focus,
-.ui-widget-header .ui-state-focus {
- border: 1px solid #999999/*{borderColorHover}*/;
- background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/;
- font-weight: normal/*{fwDefault}*/;
- color: #212121/*{fcHover}*/;
-}
-.ui-state-hover a,
-.ui-state-hover a:hover,
-.ui-state-hover a:link,
-.ui-state-hover a:visited {
- color: #212121/*{fcHover}*/;
- text-decoration: none;
-}
-.ui-state-active,
-.ui-widget-content .ui-state-active,
-.ui-widget-header .ui-state-active {
- border: 1px solid #aaaaaa/*{borderColorActive}*/;
- background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/;
- font-weight: normal/*{fwDefault}*/;
- color: #212121/*{fcActive}*/;
-}
-.ui-state-active a,
-.ui-state-active a:link,
-.ui-state-active a:visited {
- color: #212121/*{fcActive}*/;
- text-decoration: none;
-}
-
-/* Interaction Cues
-----------------------------------*/
-.ui-state-highlight,
-.ui-widget-content .ui-state-highlight,
-.ui-widget-header .ui-state-highlight {
- border: 1px solid #fcefa1/*{borderColorHighlight}*/;
- background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/;
- color: #363636/*{fcHighlight}*/;
-}
-.ui-state-highlight a,
-.ui-widget-content .ui-state-highlight a,
-.ui-widget-header .ui-state-highlight a {
- color: #363636/*{fcHighlight}*/;
-}
-.ui-state-error,
-.ui-widget-content .ui-state-error,
-.ui-widget-header .ui-state-error {
- border: 1px solid #cd0a0a/*{borderColorError}*/;
- background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/;
- color: #cd0a0a/*{fcError}*/;
-}
-.ui-state-error a,
-.ui-widget-content .ui-state-error a,
-.ui-widget-header .ui-state-error a {
- color: #cd0a0a/*{fcError}*/;
-}
-.ui-state-error-text,
-.ui-widget-content .ui-state-error-text,
-.ui-widget-header .ui-state-error-text {
- color: #cd0a0a/*{fcError}*/;
-}
-.ui-priority-primary,
-.ui-widget-content .ui-priority-primary,
-.ui-widget-header .ui-priority-primary {
- font-weight: bold;
-}
-.ui-priority-secondary,
-.ui-widget-content .ui-priority-secondary,
-.ui-widget-header .ui-priority-secondary {
- opacity: .7;
- filter:Alpha(Opacity=70);
- font-weight: normal;
-}
-.ui-state-disabled,
-.ui-widget-content .ui-state-disabled,
-.ui-widget-header .ui-state-disabled {
- opacity: .35;
- filter:Alpha(Opacity=35);
- background-image: none;
-}
-.ui-state-disabled .ui-icon {
- filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
-}
-
-/* Icons
-----------------------------------*/
-
-/* states and images */
-.ui-icon {
- width: 16px;
- height: 16px;
-}
-.ui-icon,
-.ui-widget-content .ui-icon {
- background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/;
-}
-.ui-widget-header .ui-icon {
- background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/;
-}
-.ui-state-default .ui-icon {
- background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/;
-}
-.ui-state-hover .ui-icon,
-.ui-state-focus .ui-icon {
- background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/;
-}
-.ui-state-active .ui-icon {
- background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/;
-}
-.ui-state-highlight .ui-icon {
- background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/;
-}
-.ui-state-error .ui-icon,
-.ui-state-error-text .ui-icon {
- background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/;
-}
-
-/* positioning */
-.ui-icon-blank { background-position: 16px 16px; }
-.ui-icon-carat-1-n { background-position: 0 0; }
-.ui-icon-carat-1-ne { background-position: -16px 0; }
-.ui-icon-carat-1-e { background-position: -32px 0; }
-.ui-icon-carat-1-se { background-position: -48px 0; }
-.ui-icon-carat-1-s { background-position: -64px 0; }
-.ui-icon-carat-1-sw { background-position: -80px 0; }
-.ui-icon-carat-1-w { background-position: -96px 0; }
-.ui-icon-carat-1-nw { background-position: -112px 0; }
-.ui-icon-carat-2-n-s { background-position: -128px 0; }
-.ui-icon-carat-2-e-w { background-position: -144px 0; }
-.ui-icon-triangle-1-n { background-position: 0 -16px; }
-.ui-icon-triangle-1-ne { background-position: -16px -16px; }
-.ui-icon-triangle-1-e { background-position: -32px -16px; }
-.ui-icon-triangle-1-se { background-position: -48px -16px; }
-.ui-icon-triangle-1-s { background-position: -64px -16px; }
-.ui-icon-triangle-1-sw { background-position: -80px -16px; }
-.ui-icon-triangle-1-w { background-position: -96px -16px; }
-.ui-icon-triangle-1-nw { background-position: -112px -16px; }
-.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
-.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
-.ui-icon-arrow-1-n { background-position: 0 -32px; }
-.ui-icon-arrow-1-ne { background-position: -16px -32px; }
-.ui-icon-arrow-1-e { background-position: -32px -32px; }
-.ui-icon-arrow-1-se { background-position: -48px -32px; }
-.ui-icon-arrow-1-s { background-position: -64px -32px; }
-.ui-icon-arrow-1-sw { background-position: -80px -32px; }
-.ui-icon-arrow-1-w { background-position: -96px -32px; }
-.ui-icon-arrow-1-nw { background-position: -112px -32px; }
-.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
-.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
-.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
-.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
-.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
-.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
-.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
-.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
-.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
-.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
-.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
-.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
-.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
-.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
-.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
-.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
-.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
-.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
-.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
-.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
-.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
-.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
-.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
-.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
-.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
-.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
-.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
-.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
-.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
-.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
-.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
-.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
-.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
-.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
-.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
-.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
-.ui-icon-arrow-4 { background-position: 0 -80px; }
-.ui-icon-arrow-4-diag { background-position: -16px -80px; }
-.ui-icon-extlink { background-position: -32px -80px; }
-.ui-icon-newwin { background-position: -48px -80px; }
-.ui-icon-refresh { background-position: -64px -80px; }
-.ui-icon-shuffle { background-position: -80px -80px; }
-.ui-icon-transfer-e-w { background-position: -96px -80px; }
-.ui-icon-transferthick-e-w { background-position: -112px -80px; }
-.ui-icon-folder-collapsed { background-position: 0 -96px; }
-.ui-icon-folder-open { background-position: -16px -96px; }
-.ui-icon-document { background-position: -32px -96px; }
-.ui-icon-document-b { background-position: -48px -96px; }
-.ui-icon-note { background-position: -64px -96px; }
-.ui-icon-mail-closed { background-position: -80px -96px; }
-.ui-icon-mail-open { background-position: -96px -96px; }
-.ui-icon-suitcase { background-position: -112px -96px; }
-.ui-icon-comment { background-position: -128px -96px; }
-.ui-icon-person { background-position: -144px -96px; }
-.ui-icon-print { background-position: -160px -96px; }
-.ui-icon-trash { background-position: -176px -96px; }
-.ui-icon-locked { background-position: -192px -96px; }
-.ui-icon-unlocked { background-position: -208px -96px; }
-.ui-icon-bookmark { background-position: -224px -96px; }
-.ui-icon-tag { background-position: -240px -96px; }
-.ui-icon-home { background-position: 0 -112px; }
-.ui-icon-flag { background-position: -16px -112px; }
-.ui-icon-calendar { background-position: -32px -112px; }
-.ui-icon-cart { background-position: -48px -112px; }
-.ui-icon-pencil { background-position: -64px -112px; }
-.ui-icon-clock { background-position: -80px -112px; }
-.ui-icon-disk { background-position: -96px -112px; }
-.ui-icon-calculator { background-position: -112px -112px; }
-.ui-icon-zoomin { background-position: -128px -112px; }
-.ui-icon-zoomout { background-position: -144px -112px; }
-.ui-icon-search { background-position: -160px -112px; }
-.ui-icon-wrench { background-position: -176px -112px; }
-.ui-icon-gear { background-position: -192px -112px; }
-.ui-icon-heart { background-position: -208px -112px; }
-.ui-icon-star { background-position: -224px -112px; }
-.ui-icon-link { background-position: -240px -112px; }
-.ui-icon-cancel { background-position: 0 -128px; }
-.ui-icon-plus { background-position: -16px -128px; }
-.ui-icon-plusthick { background-position: -32px -128px; }
-.ui-icon-minus { background-position: -48px -128px; }
-.ui-icon-minusthick { background-position: -64px -128px; }
-.ui-icon-close { background-position: -80px -128px; }
-.ui-icon-closethick { background-position: -96px -128px; }
-.ui-icon-key { background-position: -112px -128px; }
-.ui-icon-lightbulb { background-position: -128px -128px; }
-.ui-icon-scissors { background-position: -144px -128px; }
-.ui-icon-clipboard { background-position: -160px -128px; }
-.ui-icon-copy { background-position: -176px -128px; }
-.ui-icon-contact { background-position: -192px -128px; }
-.ui-icon-image { background-position: -208px -128px; }
-.ui-icon-video { background-position: -224px -128px; }
-.ui-icon-script { background-position: -240px -128px; }
-.ui-icon-alert { background-position: 0 -144px; }
-.ui-icon-info { background-position: -16px -144px; }
-.ui-icon-notice { background-position: -32px -144px; }
-.ui-icon-help { background-position: -48px -144px; }
-.ui-icon-check { background-position: -64px -144px; }
-.ui-icon-bullet { background-position: -80px -144px; }
-.ui-icon-radio-on { background-position: -96px -144px; }
-.ui-icon-radio-off { background-position: -112px -144px; }
-.ui-icon-pin-w { background-position: -128px -144px; }
-.ui-icon-pin-s { background-position: -144px -144px; }
-.ui-icon-play { background-position: 0 -160px; }
-.ui-icon-pause { background-position: -16px -160px; }
-.ui-icon-seek-next { background-position: -32px -160px; }
-.ui-icon-seek-prev { background-position: -48px -160px; }
-.ui-icon-seek-end { background-position: -64px -160px; }
-.ui-icon-seek-start { background-position: -80px -160px; }
-/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
-.ui-icon-seek-first { background-position: -80px -160px; }
-.ui-icon-stop { background-position: -96px -160px; }
-.ui-icon-eject { background-position: -112px -160px; }
-.ui-icon-volume-off { background-position: -128px -160px; }
-.ui-icon-volume-on { background-position: -144px -160px; }
-.ui-icon-power { background-position: 0 -176px; }
-.ui-icon-signal-diag { background-position: -16px -176px; }
-.ui-icon-signal { background-position: -32px -176px; }
-.ui-icon-battery-0 { background-position: -48px -176px; }
-.ui-icon-battery-1 { background-position: -64px -176px; }
-.ui-icon-battery-2 { background-position: -80px -176px; }
-.ui-icon-battery-3 { background-position: -96px -176px; }
-.ui-icon-circle-plus { background-position: 0 -192px; }
-.ui-icon-circle-minus { background-position: -16px -192px; }
-.ui-icon-circle-close { background-position: -32px -192px; }
-.ui-icon-circle-triangle-e { background-position: -48px -192px; }
-.ui-icon-circle-triangle-s { background-position: -64px -192px; }
-.ui-icon-circle-triangle-w { background-position: -80px -192px; }
-.ui-icon-circle-triangle-n { background-position: -96px -192px; }
-.ui-icon-circle-arrow-e { background-position: -112px -192px; }
-.ui-icon-circle-arrow-s { background-position: -128px -192px; }
-.ui-icon-circle-arrow-w { background-position: -144px -192px; }
-.ui-icon-circle-arrow-n { background-position: -160px -192px; }
-.ui-icon-circle-zoomin { background-position: -176px -192px; }
-.ui-icon-circle-zoomout { background-position: -192px -192px; }
-.ui-icon-circle-check { background-position: -208px -192px; }
-.ui-icon-circlesmall-plus { background-position: 0 -208px; }
-.ui-icon-circlesmall-minus { background-position: -16px -208px; }
-.ui-icon-circlesmall-close { background-position: -32px -208px; }
-.ui-icon-squaresmall-plus { background-position: -48px -208px; }
-.ui-icon-squaresmall-minus { background-position: -64px -208px; }
-.ui-icon-squaresmall-close { background-position: -80px -208px; }
-.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
-.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
-.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
-.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
-.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
-.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
-
-
-/* Misc visuals
-----------------------------------*/
-
-/* Corner radius */
-.ui-corner-all,
-.ui-corner-top,
-.ui-corner-left,
-.ui-corner-tl {
- border-top-left-radius: 4px/*{cornerRadius}*/;
-}
-.ui-corner-all,
-.ui-corner-top,
-.ui-corner-right,
-.ui-corner-tr {
- border-top-right-radius: 4px/*{cornerRadius}*/;
-}
-.ui-corner-all,
-.ui-corner-bottom,
-.ui-corner-left,
-.ui-corner-bl {
- border-bottom-left-radius: 4px/*{cornerRadius}*/;
-}
-.ui-corner-all,
-.ui-corner-bottom,
-.ui-corner-right,
-.ui-corner-br {
- border-bottom-right-radius: 4px/*{cornerRadius}*/;
-}
-
-/* Overlays */
-.ui-widget-overlay {
- background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/;
- opacity: .3/*{opacityOverlay}*/;
- filter: Alpha(Opacity=30)/*{opacityFilterOverlay}*/;
-}
-.ui-widget-shadow {
- margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/;
- padding: 8px/*{thicknessShadow}*/;
- background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/;
- opacity: .3/*{opacityShadow}*/;
- filter: Alpha(Opacity=30)/*{opacityFilterShadow}*/;
- border-radius: 8px/*{cornerRadiusShadow}*/;
-}
diff --git a/framework/yii/jui/assets/themes/base/jquery.ui.tooltip.css b/framework/yii/jui/assets/themes/base/jquery.ui.tooltip.css
deleted file mode 100644
index d7632a4..0000000
--- a/framework/yii/jui/assets/themes/base/jquery.ui.tooltip.css
+++ /dev/null
@@ -1,19 +0,0 @@
-/*!
- * jQuery UI Tooltip 1.10.3
- * http://jqueryui.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-.ui-tooltip {
- padding: 8px;
- position: absolute;
- z-index: 9999;
- max-width: 300px;
- -webkit-box-shadow: 0 0 5px #aaa;
- box-shadow: 0 0 5px #aaa;
-}
-body .ui-tooltip {
- border-width: 2px;
-}
diff --git a/framework/yii/log/DbTarget.php b/framework/yii/log/DbTarget.php
new file mode 100644
index 0000000..b649e9e
--- /dev/null
+++ b/framework/yii/log/DbTarget.php
@@ -0,0 +1,91 @@
+
+ * @since 2.0
+ */
+class DbTarget extends Target
+{
+ /**
+ * @var Connection|string the DB connection object or the application component ID of the DB connection.
+ * After the DbTarget object is created, if you want to change this property, you should only assign it
+ * with a DB connection object.
+ */
+ public $db = 'db';
+ /**
+ * @var string name of the DB table to store cache content.
+ * The table should be pre-created as follows:
+ *
+ * ~~~
+ * CREATE TABLE tbl_log (
+ * id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ * level INTEGER,
+ * category VARCHAR(255),
+ * log_time INTEGER,
+ * message TEXT,
+ * INDEX idx_log_level (level),
+ * INDEX idx_log_category (category)
+ * )
+ * ~~~
+ *
+ * Note that the 'id' column must be created as an auto-incremental column.
+ * The above SQL uses the MySQL syntax. If you are using other DBMS, you need
+ * to adjust it accordingly. For example, in PostgreSQL, it should be `id SERIAL PRIMARY KEY`.
+ *
+ * The indexes declared above are not required. They are mainly used to improve the performance
+ * of some queries about message levels and categories. Depending on your actual needs, you may
+ * want to create additional indexes (e.g. index on `log_time`).
+ */
+ public $logTable = 'tbl_log';
+
+ /**
+ * Initializes the DbTarget component.
+ * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
+ * @throws InvalidConfigException if [[db]] is invalid.
+ */
+ public function init()
+ {
+ parent::init();
+ if (is_string($this->db)) {
+ $this->db = Yii::$app->getComponent($this->db);
+ }
+ if (!$this->db instanceof Connection) {
+ throw new InvalidConfigException("DbTarget::db must be either a DB connection instance or the application component ID of a DB connection.");
+ }
+ }
+
+ /**
+ * Stores log messages to DB.
+ */
+ public function export()
+ {
+ $tableName = $this->db->quoteTableName($this->logTable);
+ $sql = "INSERT INTO $tableName ([[level]], [[category]], [[log_time]], [[message]])
+ VALUES (:level, :category, :log_time, :message)";
+ $command = $this->db->createCommand($sql);
+ foreach ($this->messages as $message) {
+ $command->bindValues([
+ ':level' => $message[1],
+ ':category' => $message[2],
+ ':log_time' => $message[3],
+ ':message' => $message[0],
+ ])->execute();
+ }
+ }
+}
diff --git a/framework/yii/log/EmailTarget.php b/framework/yii/log/EmailTarget.php
new file mode 100644
index 0000000..f3bd98f
--- /dev/null
+++ b/framework/yii/log/EmailTarget.php
@@ -0,0 +1,81 @@
+
+ * @since 2.0
+ */
+class EmailTarget extends Target
+{
+ /**
+ * @var array the configuration array for creating a [[\yii\mail\MessageInterface|message]] object.
+ * Note that the "to" option must be set, which specifies the destination email address(es).
+ */
+ public $message = [];
+ /**
+ * @var MailerInterface|string the mailer object or the application component ID of the mailer object.
+ * After the EmailTarget object is created, if you want to change this property, you should only assign it
+ * with a mailer object.
+ */
+ public $mail = 'mail';
+
+ /**
+ * @inheritdoc
+ */
+ public function init()
+ {
+ parent::init();
+ if (empty($this->message['to'])) {
+ throw new InvalidConfigException('The "to" option must be set for EmailTarget::message.');
+ }
+ if (is_string($this->mail)) {
+ $this->mail = Yii::$app->getComponent($this->mail);
+ }
+ if (!$this->mail instanceof MailerInterface) {
+ throw new InvalidConfigException("EmailTarget::mailer must be either a mailer object or the application component ID of a mailer object.");
+ }
+ }
+
+ /**
+ * Sends log messages to specified email addresses.
+ */
+ public function export()
+ {
+ // moved initialization of subject here because of the following issue
+ // https://github.com/yiisoft/yii2/issues/1446
+ if (empty($this->message['subject'])) {
+ $this->message['subject'] = Yii::t('yii', 'Application Log');
+ }
+ $messages = array_map([$this, 'formatMessage'], $this->messages);
+ $body = wordwrap(implode("\n", $messages), 70);
+ $this->composeMessage($body)->send($this->mail);
+ }
+
+ /**
+ * Composes a mail message with the given body content.
+ * @param string $body the body content
+ * @return \yii\mail\MessageInterface $message
+ */
+ protected function composeMessage($body)
+ {
+ $message = $this->mail->compose();
+ Yii::configure($message, $this->message);
+ $message->setTextBody($body);
+ return $message;
+ }
+}
diff --git a/framework/yii/log/FileTarget.php b/framework/yii/log/FileTarget.php
new file mode 100644
index 0000000..4382aa5
--- /dev/null
+++ b/framework/yii/log/FileTarget.php
@@ -0,0 +1,127 @@
+
+ * @since 2.0
+ */
+class FileTarget extends Target
+{
+ /**
+ * @var string log file path or path alias. If not set, it will use the "runtime/logs/app.log" file.
+ * The directory containing the log files will be automatically created if not existing.
+ */
+ public $logFile;
+ /**
+ * @var integer maximum log file size, in kilo-bytes. Defaults to 10240, meaning 10MB.
+ */
+ public $maxFileSize = 10240; // in KB
+ /**
+ * @var integer number of log files used for rotation. Defaults to 5.
+ */
+ public $maxLogFiles = 5;
+ /**
+ * @var integer the permission to be set for newly created log files.
+ * This value will be used by PHP chmod() function. No umask will be applied.
+ * If not set, the permission will be determined by the current environment.
+ */
+ public $fileMode;
+ /**
+ * @var integer the permission to be set for newly created directories.
+ * This value will be used by PHP chmod() function. No umask will be applied.
+ * Defaults to 0775, meaning the directory is read-writable by owner and group,
+ * but read-only for other users.
+ */
+ public $dirMode = 0775;
+
+
+ /**
+ * Initializes the route.
+ * This method is invoked after the route is created by the route manager.
+ */
+ public function init()
+ {
+ parent::init();
+ if ($this->logFile === null) {
+ $this->logFile = Yii::$app->getRuntimePath() . '/logs/app.log';
+ } else {
+ $this->logFile = Yii::getAlias($this->logFile);
+ }
+ $logPath = dirname($this->logFile);
+ if (!is_dir($logPath)) {
+ FileHelper::createDirectory($logPath, $this->dirMode, true);
+ }
+ if ($this->maxLogFiles < 1) {
+ $this->maxLogFiles = 1;
+ }
+ if ($this->maxFileSize < 1) {
+ $this->maxFileSize = 1;
+ }
+ }
+
+ /**
+ * Writes log messages to a file.
+ * @throws InvalidConfigException if unable to open the log file for writing
+ */
+ public function export()
+ {
+ $text = implode("\n", array_map([$this, 'formatMessage'], $this->messages)) . "\n";
+ if (($fp = @fopen($this->logFile, 'a')) === false) {
+ throw new InvalidConfigException("Unable to append to log file: {$this->logFile}");
+ }
+ @flock($fp, LOCK_EX);
+ if (@filesize($this->logFile) > $this->maxFileSize * 1024) {
+ $this->rotateFiles();
+ @flock($fp, LOCK_UN);
+ @fclose($fp);
+ @file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX);
+ } else {
+ @fwrite($fp, $text);
+ @flock($fp, LOCK_UN);
+ @fclose($fp);
+ }
+ if ($this->fileMode !== null) {
+ @chmod($this->logFile, $this->fileMode);
+ }
+ }
+
+ /**
+ * Rotates log files.
+ */
+ protected function rotateFiles()
+ {
+ $file = $this->logFile;
+ for ($i = $this->maxLogFiles; $i > 0; --$i) {
+ $rotateFile = $file . '.' . $i;
+ if (is_file($rotateFile)) {
+ // suppress errors because it's possible multiple processes enter into this section
+ if ($i === $this->maxLogFiles) {
+ @unlink($rotateFile);
+ } else {
+ @rename($rotateFile, $file . '.' . ($i + 1));
+ }
+ }
+ }
+ if (is_file($file)) {
+ @rename($file, $file . '.1'); // suppress errors because it's possible multiple processes enter into this section
+ }
+ }
+}
diff --git a/framework/yii/log/Logger.php b/framework/yii/log/Logger.php
new file mode 100644
index 0000000..1629b9b
--- /dev/null
+++ b/framework/yii/log/Logger.php
@@ -0,0 +1,345 @@
+log`.
+ * You can call the method [[log()]] to record a single log message. For convenience, a set of shortcut
+ * methods are provided for logging messages of various severity levels via the [[Yii]] class:
+ *
+ * - [[Yii::trace()]]
+ * - [[Yii::error()]]
+ * - [[Yii::warning()]]
+ * - [[Yii::info()]]
+ * - [[Yii::beginProfile()]]
+ * - [[Yii::endProfile()]]
+ *
+ * When enough messages are accumulated in the logger, or when the current request finishes,
+ * the logged messages will be sent to different [[targets]], such as log files, emails.
+ *
+ * You may configure the targets in application configuration, like the following:
+ *
+ * ~~~
+ * [
+ * 'components' => [
+ * 'log' => [
+ * 'targets' => [
+ * 'file' => [
+ * 'class' => 'yii\log\FileTarget',
+ * 'levels' => ['trace', 'info'],
+ * 'categories' => ['yii\*'],
+ * ],
+ * 'email' => [
+ * 'class' => 'yii\log\EmailTarget',
+ * 'levels' => ['error', 'warning'],
+ * 'message' => [
+ * 'to' => 'admin@example.com',
+ * ],
+ * ],
+ * ],
+ * ],
+ * ],
+ * ]
+ * ~~~
+ *
+ * Each log target can have a name and can be referenced via the [[targets]] property
+ * as follows:
+ *
+ * ~~~
+ * Yii::$app->log->targets['file']->enabled = false;
+ * ~~~
+ *
+ * When the application ends or [[flushInterval]] is reached, Logger will call [[flush()]]
+ * to send logged messages to different log targets, such as file, email, Web.
+ *
+ * @property array $dbProfiling The first element indicates the number of SQL statements executed, and the
+ * second element the total time spent in SQL execution. This property is read-only.
+ * @property float $elapsedTime The total elapsed time in seconds for current request. This property is
+ * read-only.
+ * @property array $profiling The profiling results. Each array element has the following structure: `[$token,
+ * $category, $time]`. This property is read-only.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class Logger extends Component
+{
+ /**
+ * Error message level. An error message is one that indicates the abnormal termination of the
+ * application and may require developer's handling.
+ */
+ const LEVEL_ERROR = 0x01;
+ /**
+ * Warning message level. A warning message is one that indicates some abnormal happens but
+ * the application is able to continue to run. Developers should pay attention to this message.
+ */
+ const LEVEL_WARNING = 0x02;
+ /**
+ * Informational message level. An informational message is one that includes certain information
+ * for developers to review.
+ */
+ const LEVEL_INFO = 0x04;
+ /**
+ * Tracing message level. An tracing message is one that reveals the code execution flow.
+ */
+ const LEVEL_TRACE = 0x08;
+ /**
+ * Profiling message level. This indicates the message is for profiling purpose.
+ */
+ const LEVEL_PROFILE = 0x40;
+ /**
+ * Profiling message level. This indicates the message is for profiling purpose. It marks the
+ * beginning of a profiling block.
+ */
+ const LEVEL_PROFILE_BEGIN = 0x50;
+ /**
+ * Profiling message level. This indicates the message is for profiling purpose. It marks the
+ * end of a profiling block.
+ */
+ const LEVEL_PROFILE_END = 0x60;
+
+
+ /**
+ * @var array logged messages. This property is managed by [[log()]] and [[flush()]].
+ * Each log message is of the following structure:
+ *
+ * ~~~
+ * [
+ * [0] => message (mixed, can be a string or some complex data, such as an exception object)
+ * [1] => level (integer)
+ * [2] => category (string)
+ * [3] => timestamp (float, obtained by microtime(true))
+ * [4] => traces (array, debug backtrace, contains the application code call stacks)
+ * ]
+ * ~~~
+ */
+ public $messages = [];
+ /**
+ * @var array debug data. This property stores various types of debug data reported at
+ * different instrument places.
+ */
+ public $data = [];
+ /**
+ * @var array|Target[] the log targets. Each array element represents a single [[Target|log target]] instance
+ * or the configuration for creating the log target instance.
+ */
+ public $targets = [];
+ /**
+ * @var integer how many messages should be logged before they are flushed from memory and sent to targets.
+ * Defaults to 1000, meaning the [[flush]] method will be invoked once every 1000 messages logged.
+ * Set this property to be 0 if you don't want to flush messages until the application terminates.
+ * This property mainly affects how much memory will be taken by the logged messages.
+ * A smaller value means less memory, but will increase the execution time due to the overhead of [[flush()]].
+ */
+ public $flushInterval = 1000;
+ /**
+ * @var integer how much call stack information (file name and line number) should be logged for each message.
+ * If it is greater than 0, at most that number of call stacks will be logged. Note that only application
+ * call stacks are counted.
+ *
+ * If not set, it will default to 3 when `YII_ENV` is set as "dev", and 0 otherwise.
+ */
+ public $traceLevel;
+
+ /**
+ * Initializes the logger by registering [[flush()]] as a shutdown function.
+ */
+ public function init()
+ {
+ parent::init();
+ if ($this->traceLevel === null) {
+ $this->traceLevel = YII_ENV_DEV ? 3 : 0;
+ }
+ foreach ($this->targets as $name => $target) {
+ if (!$target instanceof Target) {
+ $this->targets[$name] = Yii::createObject($target);
+ }
+ }
+ register_shutdown_function([$this, 'flush'], true);
+ }
+
+ /**
+ * Logs a message with the given type and category.
+ * If [[traceLevel]] is greater than 0, additional call stack information about
+ * the application code will be logged as well.
+ * @param string $message the message to be logged.
+ * @param integer $level the level of the message. This must be one of the following:
+ * `Logger::LEVEL_ERROR`, `Logger::LEVEL_WARNING`, `Logger::LEVEL_INFO`, `Logger::LEVEL_TRACE`,
+ * `Logger::LEVEL_PROFILE_BEGIN`, `Logger::LEVEL_PROFILE_END`.
+ * @param string $category the category of the message.
+ */
+ public function log($message, $level, $category = 'application')
+ {
+ $time = microtime(true);
+ $traces = [];
+ if ($this->traceLevel > 0) {
+ $count = 0;
+ $ts = debug_backtrace();
+ array_pop($ts); // remove the last trace since it would be the entry script, not very useful
+ foreach ($ts as $trace) {
+ if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII_PATH) !== 0) {
+ unset($trace['object'], $trace['args']);
+ $traces[] = $trace;
+ if (++$count >= $this->traceLevel) {
+ break;
+ }
+ }
+ }
+ }
+ $this->messages[] = [$message, $level, $category, $time, $traces];
+ if ($this->flushInterval > 0 && count($this->messages) >= $this->flushInterval) {
+ $this->flush();
+ }
+ }
+
+ /**
+ * Flushes log messages from memory to targets.
+ * @param boolean $final whether this is a final call during a request.
+ */
+ public function flush($final = false)
+ {
+ /** @var Target $target */
+ foreach ($this->targets as $target) {
+ if ($target->enabled) {
+ $target->collect($this->messages, $final);
+ }
+ }
+ $this->messages = [];
+ }
+
+ /**
+ * Returns the total elapsed time since the start of the current request.
+ * This method calculates the difference between now and the timestamp
+ * defined by constant `YII_BEGIN_TIME` which is evaluated at the beginning
+ * of [[BaseYii]] class file.
+ * @return float the total elapsed time in seconds for current request.
+ */
+ public function getElapsedTime()
+ {
+ return microtime(true) - YII_BEGIN_TIME;
+ }
+
+ /**
+ * Returns the profiling results.
+ *
+ * By default, all profiling results will be returned. You may provide
+ * `$categories` and `$excludeCategories` as parameters to retrieve the
+ * results that you are interested in.
+ *
+ * @param array $categories list of categories that you are interested in.
+ * You can use an asterisk at the end of a category to do a prefix match.
+ * For example, 'yii\db\*' will match categories starting with 'yii\db\',
+ * such as 'yii\db\Connection'.
+ * @param array $excludeCategories list of categories that you want to exclude
+ * @return array the profiling results. Each array element has the following structure:
+ * `[$token, $category, $time]`.
+ */
+ public function getProfiling($categories = [], $excludeCategories = [])
+ {
+ $timings = $this->calculateTimings();
+ if (empty($categories) && empty($excludeCategories)) {
+ return $timings;
+ }
+
+ foreach ($timings as $i => $timing) {
+ $matched = empty($categories);
+ foreach ($categories as $category) {
+ $prefix = rtrim($category, '*');
+ if (strpos($timing[1], $prefix) === 0 && ($timing[1] === $category || $prefix !== $category)) {
+ $matched = true;
+ break;
+ }
+ }
+
+ if ($matched) {
+ foreach ($excludeCategories as $category) {
+ $prefix = rtrim($category, '*');
+ foreach ($timings as $i => $timing) {
+ if (strpos($timing[1], $prefix) === 0 && ($timing[1] === $category || $prefix !== $category)) {
+ $matched = false;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!$matched) {
+ unset($timings[$i]);
+ }
+ }
+ return array_values($timings);
+ }
+
+ /**
+ * Returns the statistical results of DB queries.
+ * The results returned include the number of SQL statements executed and
+ * the total time spent.
+ * @return array the first element indicates the number of SQL statements executed,
+ * and the second element the total time spent in SQL execution.
+ */
+ public function getDbProfiling()
+ {
+ $timings = $this->getProfiling(['yii\db\Command::query', 'yii\db\Command::execute']);
+ $count = count($timings);
+ $time = 0;
+ foreach ($timings as $timing) {
+ $time += $timing[1];
+ }
+ return [$count, $time];
+ }
+
+ private function calculateTimings()
+ {
+ $timings = [];
+ $stack = [];
+ foreach ($this->messages as $log) {
+ list($token, $level, $category, $timestamp) = $log;
+ if ($level == self::LEVEL_PROFILE_BEGIN) {
+ $stack[] = $log;
+ } elseif ($level == self::LEVEL_PROFILE_END) {
+ if (($last = array_pop($stack)) !== null && $last[0] === $token) {
+ $timings[] = [$token, $category, $timestamp - $last[3]];
+ } else {
+ throw new InvalidConfigException("Unmatched profiling block: $token");
+ }
+ }
+ }
+
+ $now = microtime(true);
+ while (($last = array_pop($stack)) !== null) {
+ $delta = $now - $last[3];
+ $timings[] = [$last[0], $last[2], $delta];
+ }
+
+ return $timings;
+ }
+
+ /**
+ * Returns the text display of the specified level.
+ * @param integer $level the message level, e.g. [[LEVEL_ERROR]], [[LEVEL_WARNING]].
+ * @return string the text display of the level
+ */
+ public static function getLevelName($level)
+ {
+ static $levels = [
+ self::LEVEL_ERROR => 'error',
+ self::LEVEL_WARNING => 'warning',
+ self::LEVEL_INFO => 'info',
+ self::LEVEL_TRACE => 'trace',
+ self::LEVEL_PROFILE_BEGIN => 'profile begin',
+ self::LEVEL_PROFILE_END => 'profile end',
+ ];
+ return isset($levels[$level]) ? $levels[$level] : 'unknown';
+ }
+}
diff --git a/framework/yii/log/Target.php b/framework/yii/log/Target.php
new file mode 100644
index 0000000..d5d8e5b
--- /dev/null
+++ b/framework/yii/log/Target.php
@@ -0,0 +1,238 @@
+
+ * @since 2.0
+ */
+abstract class Target extends Component
+{
+ /**
+ * @var boolean whether to enable this log target. Defaults to true.
+ */
+ public $enabled = true;
+ /**
+ * @var array list of message categories that this target is interested in. Defaults to empty, meaning all categories.
+ * You can use an asterisk at the end of a category so that the category may be used to
+ * match those categories sharing the same common prefix. For example, 'yii\db\*' will match
+ * categories starting with 'yii\db\', such as 'yii\db\Connection'.
+ */
+ public $categories = [];
+ /**
+ * @var array list of message categories that this target is NOT interested in. Defaults to empty, meaning no uninteresting messages.
+ * If this property is not empty, then any category listed here will be excluded from [[categories]].
+ * You can use an asterisk at the end of a category so that the category can be used to
+ * match those categories sharing the same common prefix. For example, 'yii\db\*' will match
+ * categories starting with 'yii\db\', such as 'yii\db\Connection'.
+ * @see categories
+ */
+ public $except = [];
+ /**
+ * @var boolean whether to log a message containing the current user name and ID. Defaults to false.
+ * @see \yii\web\User
+ */
+ public $logUser = false;
+ /**
+ * @var array list of the PHP predefined variables that should be logged in a message.
+ * Note that a variable must be accessible via `$GLOBALS`. Otherwise it won't be logged.
+ * Defaults to `['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER']`.
+ */
+ public $logVars = ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER'];
+ /**
+ * @var integer how many messages should be accumulated before they are exported.
+ * Defaults to 1000. Note that messages will always be exported when the application terminates.
+ * Set this property to be 0 if you don't want to export messages until the application terminates.
+ */
+ public $exportInterval = 1000;
+ /**
+ * @var array the messages that are retrieved from the logger so far by this log target.
+ * Please refer to [[Logger::messages]] for the details about the message structure.
+ */
+ public $messages = [];
+
+ private $_levels = 0;
+
+ /**
+ * Exports log [[messages]] to a specific destination.
+ * Child classes must implement this method.
+ */
+ abstract public function export();
+
+ /**
+ * Processes the given log messages.
+ * This method will filter the given messages with [[levels]] and [[categories]].
+ * And if requested, it will also export the filtering result to specific medium (e.g. email).
+ * @param array $messages log messages to be processed. See [[Logger::messages]] for the structure
+ * of each message.
+ * @param boolean $final whether this method is called at the end of the current application
+ */
+ public function collect($messages, $final)
+ {
+ $this->messages = array_merge($this->messages, $this->filterMessages($messages, $this->getLevels(), $this->categories, $this->except));
+ $count = count($this->messages);
+ if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) {
+ if (($context = $this->getContextMessage()) !== '') {
+ $this->messages[] = [$context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME];
+ }
+ $this->export();
+ $this->messages = [];
+ }
+ }
+
+ /**
+ * Generates the context information to be logged.
+ * The default implementation will dump user information, system variables, etc.
+ * @return string the context information. If an empty string, it means no context information.
+ */
+ protected function getContextMessage()
+ {
+ $context = [];
+ if ($this->logUser && ($user = Yii::$app->getComponent('user', false)) !== null) {
+ /** @var \yii\web\User $user */
+ $context[] = 'User: ' . $user->getId();
+ }
+
+ foreach ($this->logVars as $name) {
+ if (!empty($GLOBALS[$name])) {
+ $context[] = "\${$name} = " . var_export($GLOBALS[$name], true);
+ }
+ }
+
+ return implode("\n\n", $context);
+ }
+
+ /**
+ * @return integer the message levels that this target is interested in. This is a bitmap of
+ * level values. Defaults to 0, meaning all available levels.
+ */
+ public function getLevels()
+ {
+ return $this->_levels;
+ }
+
+ /**
+ * Sets the message levels that this target is interested in.
+ *
+ * The parameter can be either an array of interested level names or an integer representing
+ * the bitmap of the interested level values. Valid level names include: 'error',
+ * 'warning', 'info', 'trace' and 'profile'; valid level values include:
+ * [[Logger::LEVEL_ERROR]], [[Logger::LEVEL_WARNING]], [[Logger::LEVEL_INFO]],
+ * [[Logger::LEVEL_TRACE]] and [[Logger::LEVEL_PROFILE]].
+ *
+ * For example,
+ *
+ * ~~~
+ * ['error', 'warning']
+ * // which is equivalent to:
+ * Logger::LEVEL_ERROR | Logger::LEVEL_WARNING
+ * ~~~
+ *
+ * @param array|integer $levels message levels that this target is interested in.
+ * @throws InvalidConfigException if an unknown level name is given
+ */
+ public function setLevels($levels)
+ {
+ static $levelMap = [
+ 'error' => Logger::LEVEL_ERROR,
+ 'warning' => Logger::LEVEL_WARNING,
+ 'info' => Logger::LEVEL_INFO,
+ 'trace' => Logger::LEVEL_TRACE,
+ 'profile' => Logger::LEVEL_PROFILE,
+ ];
+ if (is_array($levels)) {
+ $this->_levels = 0;
+ foreach ($levels as $level) {
+ if (isset($levelMap[$level])) {
+ $this->_levels |= $levelMap[$level];
+ } else {
+ throw new InvalidConfigException("Unrecognized level: $level");
+ }
+ }
+ } else {
+ $this->_levels = $levels;
+ }
+ }
+
+ /**
+ * Filters the given messages according to their categories and levels.
+ * @param array $messages messages to be filtered
+ * @param integer $levels the message levels to filter by. This is a bitmap of
+ * level values. Value 0 means allowing all levels.
+ * @param array $categories the message categories to filter by. If empty, it means all categories are allowed.
+ * @param array $except the message categories to exclude. If empty, it means all categories are allowed.
+ * @return array the filtered messages.
+ */
+ public static function filterMessages($messages, $levels = 0, $categories = [], $except = [])
+ {
+ foreach ($messages as $i => $message) {
+ if ($levels && !($levels & $message[1])) {
+ unset($messages[$i]);
+ continue;
+ }
+
+ $matched = empty($categories);
+ foreach ($categories as $category) {
+ if ($message[2] === $category || substr($category, -1) === '*' && strpos($message[2], rtrim($category, '*')) === 0) {
+ $matched = true;
+ break;
+ }
+ }
+
+ if ($matched) {
+ foreach ($except as $category) {
+ $prefix = rtrim($category, '*');
+ if (strpos($message[2], $prefix) === 0 && ($message[2] === $category || $prefix !== $category)) {
+ $matched = false;
+ break;
+ }
+ }
+ }
+
+ if (!$matched) {
+ unset($messages[$i]);
+ }
+ }
+ return $messages;
+ }
+
+ /**
+ * Formats a log message.
+ * The message structure follows that in [[Logger::messages]].
+ * @param array $message the log message to be formatted.
+ * @return string the formatted message
+ */
+ public function formatMessage($message)
+ {
+ list($text, $level, $category, $timestamp) = $message;
+ $level = Logger::getLevelName($level);
+ if (!is_string($text)) {
+ $text = var_export($text, true);
+ }
+ $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
+ return date('Y/m/d H:i:s', $timestamp) . " [$ip] [$level] [$category] $text";
+ }
+}
diff --git a/framework/yii/logging/DbTarget.php b/framework/yii/logging/DbTarget.php
deleted file mode 100644
index ce9d843..0000000
--- a/framework/yii/logging/DbTarget.php
+++ /dev/null
@@ -1,93 +0,0 @@
-
- * @since 2.0
- */
-class DbTarget extends Target
-{
- /**
- * @var Connection|string the DB connection object or the application component ID of the DB connection.
- * After the DbTarget object is created, if you want to change this property, you should only assign it
- * with a DB connection object.
- */
- public $db = 'db';
- /**
- * @var string name of the DB table to store cache content.
- * The table should be pre-created as follows:
- *
- * ~~~
- * CREATE TABLE tbl_log (
- * id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
- * level INTEGER,
- * category VARCHAR(255),
- * log_time INTEGER,
- * message TEXT,
- * INDEX idx_log_level (level),
- * INDEX idx_log_category (category)
- * )
- * ~~~
- *
- * Note that the 'id' column must be created as an auto-incremental column.
- * The above SQL uses the MySQL syntax. If you are using other DBMS, you need
- * to adjust it accordingly. For example, in PostgreSQL, it should be `id SERIAL PRIMARY KEY`.
- *
- * The indexes declared above are not required. They are mainly used to improve the performance
- * of some queries about message levels and categories. Depending on your actual needs, you may
- * want to create additional indexes (e.g. index on `log_time`).
- */
- public $logTable = 'tbl_log';
-
- /**
- * Initializes the DbTarget component.
- * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
- * @throws InvalidConfigException if [[db]] is invalid.
- */
- public function init()
- {
- parent::init();
- if (is_string($this->db)) {
- $this->db = Yii::$app->getComponent($this->db);
- }
- if (!$this->db instanceof Connection) {
- throw new InvalidConfigException("DbTarget::db must be either a DB connection instance or the application component ID of a DB connection.");
- }
- }
-
- /**
- * Stores log messages to DB.
- * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure
- * of each message.
- */
- public function export($messages)
- {
- $tableName = $this->db->quoteTableName($this->logTable);
- $sql = "INSERT INTO $tableName ([[level]], [[category]], [[log_time]], [[message]])
- VALUES (:level, :category, :log_time, :message)";
- $command = $this->db->createCommand($sql);
- foreach ($messages as $message) {
- $command->bindValues(array(
- ':level' => $message[1],
- ':category' => $message[2],
- ':log_time' => $message[3],
- ':message' => $message[0],
- ))->execute();
- }
- }
-}
diff --git a/framework/yii/logging/DebugTarget.php b/framework/yii/logging/DebugTarget.php
deleted file mode 100644
index 92a74d6..0000000
--- a/framework/yii/logging/DebugTarget.php
+++ /dev/null
@@ -1,91 +0,0 @@
-
- * @since 2.0
- */
-class DebugTarget extends Target
-{
- public $maxLogFiles = 20;
-
- /**
- * Exports log messages to a specific destination.
- * Child classes must implement this method.
- * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure
- * of each message.
- */
- public function export($messages)
- {
- $path = Yii::$app->getRuntimePath() . '/debug';
- if (!is_dir($path)) {
- mkdir($path);
- }
- $file = $path . '/' . Yii::getLogger()->getTag() . '.log';
- $data = array(
- 'messages' => $messages,
- '_SERVER' => $_SERVER,
- '_GET' => $_GET,
- '_POST' => $_POST,
- '_COOKIE' => $_COOKIE,
- '_FILES' => empty($_FILES) ? array() : $_FILES,
- '_SESSION' => empty($_SESSION) ? array() : $_SESSION,
- 'memory' => memory_get_peak_usage(),
- 'time' => microtime(true) - YII_BEGIN_TIME,
- );
- file_put_contents($file, json_encode($data));
- }
-
- /**
- * Processes the given log messages.
- * This method will filter the given messages with [[levels]] and [[categories]].
- * And if requested, it will also export the filtering result to specific medium (e.g. email).
- * @param array $messages log messages to be processed. See [[Logger::messages]] for the structure
- * of each message.
- * @param boolean $final whether this method is called at the end of the current application
- */
- public function collect($messages, $final)
- {
- if (Yii::$app->getModule('debug', false) !== null) {
- return;
- }
- $this->messages = array_merge($this->messages, $this->filterMessages($messages));
- if ($final) {
- $this->export($this->messages);
- $this->gc();
- }
- }
-
- protected function gc()
- {
- if (mt_rand(0, 10000) > 100) {
- return;
- }
- $iterator = new \DirectoryIterator(Yii::$app->getRuntimePath() . '/debug');
- $files = array();
- foreach ($iterator as $file) {
- if (preg_match('/^[\d\-]+\.log$/', $file->getFileName()) && $file->isFile()) {
- $files[] = $file->getPathname();
- }
- }
- sort($files);
- if (count($files) > $this->maxLogFiles) {
- $n = count($files) - $this->maxLogFiles;
- foreach ($files as $i => $file) {
- if ($i < $n) {
- unlink($file);
- } else {
- break;
- }
- }
- }
- }
-}
diff --git a/framework/yii/logging/EmailTarget.php b/framework/yii/logging/EmailTarget.php
deleted file mode 100644
index 94e2c00..0000000
--- a/framework/yii/logging/EmailTarget.php
+++ /dev/null
@@ -1,72 +0,0 @@
-
- * @since 2.0
- */
-class EmailTarget extends Target
-{
- /**
- * @var array list of destination email addresses.
- */
- public $emails = array();
- /**
- * @var string email subject
- */
- public $subject;
- /**
- * @var string email sent-from address
- */
- public $sentFrom;
- /**
- * @var array list of additional headers to use when sending an email.
- */
- public $headers = array();
-
- /**
- * Sends log messages to specified email addresses.
- * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure
- * of each message.
- */
- public function export($messages)
- {
- $body = '';
- foreach ($messages as $message) {
- $body .= $this->formatMessage($message);
- }
- $body = wordwrap($body, 70);
- $subject = $this->subject === null ? \Yii::t('yii', 'Application Log') : $this->subject;
- foreach ($this->emails as $email) {
- $this->sendEmail($subject, $body, $email, $this->sentFrom, $this->headers);
- }
- }
-
- /**
- * Sends an email.
- * @param string $subject email subject
- * @param string $body email body
- * @param string $sentTo sent-to email address
- * @param string $sentFrom sent-from email address
- * @param array $headers additional headers to be used when sending the email
- */
- protected function sendEmail($subject, $body, $sentTo, $sentFrom, $headers)
- {
- if ($sentFrom !== null) {
- $headers[] = "From: {$sentFrom}";
- }
- mail($sentTo, $subject, $body, implode("\r\n", $headers));
- }
-}
diff --git a/framework/yii/logging/FileTarget.php b/framework/yii/logging/FileTarget.php
deleted file mode 100644
index 2db43b5..0000000
--- a/framework/yii/logging/FileTarget.php
+++ /dev/null
@@ -1,115 +0,0 @@
-
- * @since 2.0
- */
-class FileTarget extends Target
-{
- /**
- * @var string log file path or path alias. If not set, it will use the "runtime/logs/app.log" file.
- * The directory containing the log files will be automatically created if not existing.
- */
- public $logFile;
- /**
- * @var integer maximum log file size, in kilo-bytes. Defaults to 10240, meaning 10MB.
- */
- public $maxFileSize = 10240; // in KB
- /**
- * @var integer number of log files used for rotation. Defaults to 5.
- */
- public $maxLogFiles = 5;
-
-
- /**
- * Initializes the route.
- * This method is invoked after the route is created by the route manager.
- */
- public function init()
- {
- parent::init();
- if ($this->logFile === null) {
- $this->logFile = Yii::$app->getRuntimePath() . '/logs/app.log';
- } else {
- $this->logFile = Yii::getAlias($this->logFile);
- }
- $logPath = dirname($this->logFile);
- if (!is_dir($logPath)) {
- @mkdir($logPath, 0777, true);
- }
- if ($this->maxLogFiles < 1) {
- $this->maxLogFiles = 1;
- }
- if ($this->maxFileSize < 1) {
- $this->maxFileSize = 1;
- }
- }
-
- /**
- * Sends log messages to specified email addresses.
- * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure
- * of each message.
- * @throws InvalidConfigException if unable to open the log file for writing
- */
- public function export($messages)
- {
- $text = '';
- foreach ($messages as $message) {
- $text .= $this->formatMessage($message);
- }
- if (($fp = @fopen($this->logFile, 'a')) === false) {
- throw new InvalidConfigException("Unable to append to log file: {$this->logFile}");
- }
- @flock($fp, LOCK_EX);
- if (@filesize($this->logFile) > $this->maxFileSize * 1024) {
- $this->rotateFiles();
- @flock($fp, LOCK_UN);
- @fclose($fp);
- @file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX);
- } else {
- @fwrite($fp, $text);
- @flock($fp, LOCK_UN);
- @fclose($fp);
- }
- }
-
- /**
- * Rotates log files.
- */
- protected function rotateFiles()
- {
- $file = $this->logFile;
- for ($i = $this->maxLogFiles; $i > 0; --$i) {
- $rotateFile = $file . '.' . $i;
- if (is_file($rotateFile)) {
- // suppress errors because it's possible multiple processes enter into this section
- if ($i === $this->maxLogFiles) {
- @unlink($rotateFile);
- } else {
- @rename($rotateFile, $file . '.' . ($i + 1));
- }
- }
- }
- if (is_file($file)) {
- @rename($file, $file . '.1'); // suppress errors because it's possible multiple processes enter into this section
- }
- }
-}
diff --git a/framework/yii/logging/Logger.php b/framework/yii/logging/Logger.php
deleted file mode 100644
index 4bd6bcc..0000000
--- a/framework/yii/logging/Logger.php
+++ /dev/null
@@ -1,272 +0,0 @@
-
- * @since 2.0
- */
-class Logger extends Component
-{
- /**
- * Error message level. An error message is one that indicates the abnormal termination of the
- * application and may require developer's handling.
- */
- const LEVEL_ERROR = 0x01;
- /**
- * Warning message level. A warning message is one that indicates some abnormal happens but
- * the application is able to continue to run. Developers should pay attention to this message.
- */
- const LEVEL_WARNING = 0x02;
- /**
- * Informational message level. An informational message is one that includes certain information
- * for developers to review.
- */
- const LEVEL_INFO = 0x04;
- /**
- * Tracing message level. An tracing message is one that reveals the code execution flow.
- */
- const LEVEL_TRACE = 0x08;
- /**
- * Profiling message level. This indicates the message is for profiling purpose.
- */
- const LEVEL_PROFILE = 0x40;
- /**
- * Profiling message level. This indicates the message is for profiling purpose. It marks the
- * beginning of a profiling block.
- */
- const LEVEL_PROFILE_BEGIN = 0x50;
- /**
- * Profiling message level. This indicates the message is for profiling purpose. It marks the
- * end of a profiling block.
- */
- const LEVEL_PROFILE_END = 0x60;
-
-
- /**
- * @var integer how many messages should be logged before they are flushed from memory and sent to targets.
- * Defaults to 1000, meaning the [[flush]] method will be invoked once every 1000 messages logged.
- * Set this property to be 0 if you don't want to flush messages until the application terminates.
- * This property mainly affects how much memory will be taken by the logged messages.
- * A smaller value means less memory, but will increase the execution time due to the overhead of [[flush()]].
- */
- public $flushInterval = 1000;
- /**
- * @var array logged messages. This property is mainly managed by [[log()]] and [[flush()]].
- * Each log message is of the following structure:
- *
- * ~~~
- * array(
- * [0] => message (mixed, can be a string or some complex data, such as an exception object)
- * [1] => level (integer)
- * [2] => category (string)
- * [3] => timestamp (float, obtained by microtime(true))
- * )
- * ~~~
- */
- public $messages = array();
- /**
- * @var Router the log target router registered with this logger.
- */
- public $router;
-
-
- /**
- * @var string
- */
- private $_tag;
-
-
- /**
- * Initializes the logger by registering [[flush()]] as a shutdown function.
- */
- public function init()
- {
- parent::init();
- register_shutdown_function(array($this, 'flush'), true);
- }
-
- /**
- * Logs a message with the given type and category.
- * If `YII_DEBUG` is true and `YII_TRACE_LEVEL` is greater than 0, then additional
- * call stack information about application code will be appended to the message.
- * @param string $message the message to be logged.
- * @param integer $level the level of the message. This must be one of the following:
- * `Logger::LEVEL_ERROR`, `Logger::LEVEL_WARNING`, `Logger::LEVEL_INFO`, `Logger::LEVEL_TRACE`,
- * `Logger::LEVEL_PROFILE_BEGIN`, `Logger::LEVEL_PROFILE_END`.
- * @param string $category the category of the message.
- */
- public function log($message, $level, $category = 'application')
- {
- $time = microtime(true);
- if (YII_DEBUG && YII_TRACE_LEVEL > 0) {
- $traces = debug_backtrace();
- $count = 0;
- foreach ($traces as $trace) {
- if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII_PATH) !== 0) {
- $message .= "\nin {$trace['file']} ({$trace['line']})";
- if (++$count >= YII_TRACE_LEVEL) {
- break;
- }
- }
- }
- }
- $this->messages[] = array($message, $level, $category, $time);
- if ($this->flushInterval > 0 && count($this->messages) >= $this->flushInterval) {
- $this->flush();
- }
- }
-
- /**
- * Flushes log messages from memory to targets.
- * This method will trigger an [[EVENT_FLUSH]] or [[EVENT_FINAL_FLUSH]] event depending on the $final value.
- * @param boolean $final whether this is a final call during a request.
- */
- public function flush($final = false)
- {
- if ($this->router) {
- $this->router->dispatch($this->messages, $final);
- }
- $this->messages = array();
- }
-
- /**
- * @return string a tag that uniquely identifies the current request.
- */
- public function getTag()
- {
- if ($this->_tag === null) {
- $this->_tag = date('Ymd-His', microtime(true));
- }
- return $this->_tag;
- }
-
- /**
- * @param string $tag a tag that uniquely identifies the current request.
- */
- public function setTag($tag)
- {
- $this->_tag = $tag;
- }
-
- /**
- * Returns the total elapsed time since the start of the current request.
- * This method calculates the difference between now and the timestamp
- * defined by constant `YII_BEGIN_TIME` which is evaluated at the beginning
- * of [[YiiBase]] class file.
- * @return float the total elapsed time in seconds for current request.
- */
- public function getElapsedTime()
- {
- return microtime(true) - YII_BEGIN_TIME;
- }
-
- /**
- * Returns the profiling results.
- *
- * By default, all profiling results will be returned. You may provide
- * `$categories` and `$excludeCategories` as parameters to retrieve the
- * results that you are interested in.
- *
- * @param array $categories list of categories that you are interested in.
- * You can use an asterisk at the end of a category to do a prefix match.
- * For example, 'yii\db\*' will match categories starting with 'yii\db\',
- * such as 'yii\db\Connection'.
- * @param array $excludeCategories list of categories that you want to exclude
- * @return array the profiling results. Each array element has the following structure:
- * `array($token, $category, $time)`.
- */
- public function getProfiling($categories = array(), $excludeCategories = array())
- {
- $timings = $this->calculateTimings();
- if (empty($categories) && empty($excludeCategories)) {
- return $timings;
- }
-
- foreach ($timings as $i => $timing) {
- $matched = empty($categories);
- foreach ($categories as $category) {
- $prefix = rtrim($category, '*');
- if (strpos($timing[1], $prefix) === 0 && ($timing[1] === $category || $prefix !== $category)) {
- $matched = true;
- break;
- }
- }
-
- if ($matched) {
- foreach ($excludeCategories as $category) {
- $prefix = rtrim($category, '*');
- foreach ($timings as $i => $timing) {
- if (strpos($timing[1], $prefix) === 0 && ($timing[1] === $category || $prefix !== $category)) {
- $matched = false;
- break;
- }
- }
- }
- }
-
- if (!$matched) {
- unset($timings[$i]);
- }
- }
- return array_values($timings);
- }
-
- /**
- * Returns the statistical results of DB queries.
- * The results returned include the number of SQL statements executed and
- * the total time spent.
- * @return array the first element indicates the number of SQL statements executed,
- * and the second element the total time spent in SQL execution.
- */
- public function getDbProfiling()
- {
- $timings = $this->getProfiling(array('yii\db\Command::query', 'yii\db\Command::execute'));
- $count = count($timings);
- $time = 0;
- foreach ($timings as $timing) {
- $time += $timing[1];
- }
- return array($count, $time);
- }
-
- private function calculateTimings()
- {
- $timings = array();
-
- $stack = array();
- foreach ($this->messages as $log) {
- list($token, $level, $category, $timestamp) = $log;
- if ($level == self::LEVEL_PROFILE_BEGIN) {
- $stack[] = $log;
- } elseif ($level == self::LEVEL_PROFILE_END) {
- if (($last = array_pop($stack)) !== null && $last[0] === $token) {
- $timings[] = array($token, $category, $timestamp - $last[3]);
- } else {
- throw new InvalidConfigException("Unmatched profiling block: $token");
- }
- }
- }
-
- $now = microtime(true);
- while (($last = array_pop($stack)) !== null) {
- $delta = $now - $last[3];
- $timings[] = array($last[0], $last[2], $delta);
- }
-
- return $timings;
- }
-}
diff --git a/framework/yii/logging/ProfileTarget.php b/framework/yii/logging/ProfileTarget.php
deleted file mode 100644
index be1d73d..0000000
--- a/framework/yii/logging/ProfileTarget.php
+++ /dev/null
@@ -1,192 +0,0 @@
-
- * summary: list the execution time of every marked code block
- * callstack: list the mark code blocks in a hierarchical view reflecting their calling sequence.
- *
- *
- * @author Qiang Xue
- * @since 2.0
- */
-class CProfileLogRoute extends CWebLogRoute
-{
- /**
- * @var boolean whether to aggregate results according to profiling tokens.
- * If false, the results will be aggregated by categories.
- * Defaults to true. Note that this property only affects the summary report
- * that is enabled when {@link report} is 'summary'.
- */
- public $groupByToken = true;
- /**
- * @var string type of profiling report to display
- */
- private $_report = 'summary';
-
- /**
- * Initializes the route.
- * This method is invoked after the route is created by the route manager.
- */
- public function init()
- {
- $this->levels = CLogger::LEVEL_PROFILE;
- }
-
- /**
- * @return string the type of the profiling report to display. Defaults to 'summary'.
- */
- public function getReport()
- {
- return $this->_report;
- }
-
- /**
- * @param string $value the type of the profiling report to display. Valid values include 'summary' and 'callstack'.
- */
- public function setReport($value)
- {
- if ($value === 'summary' || $value === 'callstack')
- $this->_report = $value;
- else
- throw new CException(Yii::t('yii', 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".',
- array('{report}' => $value)));
- }
-
- /**
- * Displays the log messages.
- * @param array $logs list of log messages
- */
- public function processLogs($logs)
- {
- $app = \Yii::$app;
- if (!($app instanceof \yii\web\Application) || $app->getRequest()->getIsAjax())
- return;
-
- if ($this->getReport() === 'summary')
- $this->displaySummary($logs);
- else
- $this->displayCallstack($logs);
- }
-
- /**
- * Displays the callstack of the profiling procedures for display.
- * @param array $logs list of logs
- */
- protected function displayCallstack($logs)
- {
- $stack = array();
- $results = array();
- $n = 0;
- foreach ($logs as $log)
- {
- if ($log[1] !== CLogger::LEVEL_PROFILE) {
- continue;
- }
- $message = $log[0];
- if (!strncasecmp($message, 'begin:', 6)) {
- $log[0] = substr($message, 6);
- $log[4] = $n;
- $stack[] = $log;
- $n++;
- } elseif (!strncasecmp($message, 'end:', 4)) {
- $token = substr($message, 4);
- if (($last = array_pop($stack)) !== null && $last[0] === $token) {
- $delta = $log[3] - $last[3];
- $results[$last[4]] = array($token, $delta, count($stack));
- } else
- {
- throw new CException(Yii::t('yii', 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.',
- array('{token}' => $token)));
- }
- }
- }
- // remaining entries should be closed here
- $now = microtime(true);
- while (($last = array_pop($stack)) !== null) {
- $results[$last[4]] = array($last[0], $now - $last[3], count($stack));
- }
- ksort($results);
- $this->render('profile-callstack', $results);
- }
-
- /**
- * Displays the summary report of the profiling result.
- * @param array $logs list of logs
- */
- protected function displaySummary($logs)
- {
- $stack = array();
- foreach ($logs as $log)
- {
- if ($log[1] !== CLogger::LEVEL_PROFILE)
- continue;
- $message = $log[0];
- if (!strncasecmp($message, 'begin:', 6))
- {
- $log[0] = substr($message, 6);
- $stack[] = $log;
- } elseif (!strncasecmp($message, 'end:', 4))
- {
- $token = substr($message, 4);
- if (($last = array_pop($stack)) !== null && $last[0] === $token)
- {
- $delta = $log[3] - $last[3];
- if (!$this->groupByToken)
- $token = $log[2];
- if (isset($results[$token]))
- $results[$token] = $this->aggregateResult($results[$token], $delta);
- else
- $results[$token] = array($token, 1, $delta, $delta, $delta);
- } else
- throw new CException(Yii::t('yii', 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.',
- array('{token}' => $token)));
- }
- }
-
- $now = microtime(true);
- while (($last = array_pop($stack)) !== null)
- {
- $delta = $now - $last[3];
- $token = $this->groupByToken ? $last[0] : $last[2];
- if (isset($results[$token]))
- $results[$token] = $this->aggregateResult($results[$token], $delta);
- else
- $results[$token] = array($token, 1, $delta, $delta, $delta);
- }
-
- $entries = array_values($results);
- $func = create_function('$a,$b', 'return $a[4] < $b[4] ? 1 : 0;');
- usort($entries, $func);
-
- $this->render('profile-summary', $entries);
- }
-
- /**
- * Aggregates the report result.
- * @param array $result log result for this code block
- * @param float $delta time spent for this code block
- * @return array
- */
- protected function aggregateResult($result, $delta)
- {
- list($token, $calls, $min, $max, $total) = $result;
- if ($delta < $min)
- $min = $delta;
- elseif ($delta > $max)
- $max = $delta;
- $calls++;
- $total += $delta;
- return array($token, $calls, $min, $max, $total);
- }
-}
diff --git a/framework/yii/logging/Router.php b/framework/yii/logging/Router.php
deleted file mode 100644
index f544b72..0000000
--- a/framework/yii/logging/Router.php
+++ /dev/null
@@ -1,99 +0,0 @@
- array('log'),
- * 'components' => array(
- * 'log' => array(
- * 'class' => 'yii\logging\Router',
- * 'targets' => array(
- * 'file' => array(
- * 'class' => 'yii\logging\FileTarget',
- * 'levels' => array('trace', 'info'),
- * 'categories' => array('yii\*'),
- * ),
- * 'email' => array(
- * 'class' => 'yii\logging\EmailTarget',
- * 'levels' => array('error', 'warning'),
- * 'emails' => array('admin@example.com'),
- * ),
- * ),
- * ),
- * ),
- * )
- * ~~~
- *
- * Each log target can have a name and can be referenced via the [[targets]] property
- * as follows:
- *
- * ~~~
- * Yii::$app->log->targets['file']->enabled = false;
- * ~~~
- *
- * @author Qiang Xue
- * @since 2.0
- */
-class Router extends Component
-{
- /**
- * @var Target[] list of log target objects or configurations. If the latter, target objects will
- * be created in [[init()]] by calling [[Yii::createObject()]] with the corresponding object configuration.
- */
- public $targets = array();
-
- /**
- * Initializes this application component.
- * This method is invoked when the Router component is created by the application.
- * The method attaches the [[processLogs]] method to both the [[Logger::EVENT_FLUSH]] event
- * and the [[Logger::EVENT_FINAL_FLUSH]] event.
- */
- public function init()
- {
- parent::init();
- foreach ($this->targets as $name => $target) {
- if (!$target instanceof Target) {
- $this->targets[$name] = Yii::createObject($target);
- }
- }
- Yii::getLogger()->router = $this;
- }
-
- /**
- * Dispatches log messages to [[targets]].
- * This method is called by [[Logger]] when its [[Logger::flush()]] method is called.
- * It will forward the messages to each log target registered in [[targets]].
- * @param array $messages the messages to be processed
- * @param boolean $final whether this is the final call during a request cycle
- */
- public function dispatch($messages, $final = false)
- {
- foreach ($this->targets as $target) {
- if ($target->enabled) {
- $target->collect($messages, $final);
- }
- }
- }
-}
diff --git a/framework/yii/logging/Target.php b/framework/yii/logging/Target.php
deleted file mode 100644
index 7be7001..0000000
--- a/framework/yii/logging/Target.php
+++ /dev/null
@@ -1,245 +0,0 @@
-
- * @since 2.0
- */
-abstract class Target extends Component
-{
- /**
- * @var boolean whether to enable this log target. Defaults to true.
- */
- public $enabled = true;
- /**
- * @var array list of message categories that this target is interested in. Defaults to empty, meaning all categories.
- * You can use an asterisk at the end of a category so that the category may be used to
- * match those categories sharing the same common prefix. For example, 'yii\db\*' will match
- * categories starting with 'yii\db\', such as 'yii\db\Connection'.
- */
- public $categories = array();
- /**
- * @var array list of message categories that this target is NOT interested in. Defaults to empty, meaning no uninteresting messages.
- * If this property is not empty, then any category listed here will be excluded from [[categories]].
- * You can use an asterisk at the end of a category so that the category can be used to
- * match those categories sharing the same common prefix. For example, 'yii\db\*' will match
- * categories starting with 'yii\db\', such as 'yii\db\Connection'.
- * @see categories
- */
- public $except = array();
- /**
- * @var boolean whether to log a message containing the current user name and ID. Defaults to false.
- * @see \yii\web\User
- */
- public $logUser = false;
- /**
- * @var array list of the PHP predefined variables that should be logged in a message.
- * Note that a variable must be accessible via `$GLOBALS`. Otherwise it won't be logged.
- * Defaults to `array('_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER')`.
- */
- public $logVars = array('_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER');
- /**
- * @var integer how many messages should be accumulated before they are exported.
- * Defaults to 1000. Note that messages will always be exported when the application terminates.
- * Set this property to be 0 if you don't want to export messages until the application terminates.
- */
- public $exportInterval = 1000;
- /**
- * @var array the messages that are retrieved from the logger so far by this log target.
- */
- public $messages = array();
-
- private $_levels = 0;
-
- /**
- * Exports log messages to a specific destination.
- * Child classes must implement this method.
- * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure
- * of each message.
- */
- abstract public function export($messages);
-
- /**
- * Processes the given log messages.
- * This method will filter the given messages with [[levels]] and [[categories]].
- * And if requested, it will also export the filtering result to specific medium (e.g. email).
- * @param array $messages log messages to be processed. See [[Logger::messages]] for the structure
- * of each message.
- * @param boolean $final whether this method is called at the end of the current application
- */
- public function collect($messages, $final)
- {
- $this->messages = array_merge($this->messages, $this->filterMessages($messages));
- $count = count($this->messages);
- if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) {
- if (($context = $this->getContextMessage()) !== '') {
- $this->messages[] = array($context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME);
- }
- $this->export($this->messages);
- $this->messages = array();
- }
- }
-
- /**
- * Generates the context information to be logged.
- * The default implementation will dump user information, system variables, etc.
- * @return string the context information. If an empty string, it means no context information.
- */
- protected function getContextMessage()
- {
- $context = array();
- if ($this->logUser && ($user = Yii::$app->getComponent('user', false)) !== null) {
- /** @var $user \yii\web\User */
- $context[] = 'User: ' . $user->getId();
- }
-
- foreach ($this->logVars as $name) {
- if (!empty($GLOBALS[$name])) {
- $context[] = "\${$name} = " . var_export($GLOBALS[$name], true);
- }
- }
-
- return implode("\n\n", $context);
- }
-
- /**
- * @return integer the message levels that this target is interested in. This is a bitmap of
- * level values. Defaults to 0, meaning all available levels.
- */
- public function getLevels()
- {
- return $this->_levels;
- }
-
- /**
- * Sets the message levels that this target is interested in.
- *
- * The parameter can be either an array of interested level names or an integer representing
- * the bitmap of the interested level values. Valid level names include: 'error',
- * 'warning', 'info', 'trace' and 'profile'; valid level values include:
- * [[Logger::LEVEL_ERROR]], [[Logger::LEVEL_WARNING]], [[Logger::LEVEL_INFO]],
- * [[Logger::LEVEL_TRACE]] and [[Logger::LEVEL_PROFILE]].
- *
- * For example,
- *
- * ~~~
- * array('error', 'warning')
- * // which is equivalent to:
- * Logger::LEVEL_ERROR | Logger::LEVEL_WARNING
- * ~~~
- *
- * @param array|integer $levels message levels that this target is interested in.
- * @throws InvalidConfigException if an unknown level name is given
- */
- public function setLevels($levels)
- {
- static $levelMap = array(
- 'error' => Logger::LEVEL_ERROR,
- 'warning' => Logger::LEVEL_WARNING,
- 'info' => Logger::LEVEL_INFO,
- 'trace' => Logger::LEVEL_TRACE,
- 'profile' => Logger::LEVEL_PROFILE,
- );
- if (is_array($levels)) {
- $this->_levels = 0;
- foreach ($levels as $level) {
- if (isset($levelMap[$level])) {
- $this->_levels |= $levelMap[$level];
- } else {
- throw new InvalidConfigException("Unrecognized level: $level");
- }
- }
- } else {
- $this->_levels = $levels;
- }
- }
-
- /**
- * Filters the given messages according to their categories and levels.
- * @param array $messages messages to be filtered
- * @return array the filtered messages.
- * @see filterByCategory
- * @see filterByLevel
- */
- protected function filterMessages($messages)
- {
- $levels = $this->getLevels();
-
- foreach ($messages as $i => $message) {
- if ($levels && !($levels & $message[1])) {
- unset($messages[$i]);
- continue;
- }
-
- $matched = empty($this->categories);
- foreach ($this->categories as $category) {
- if ($message[2] === $category || substr($category, -1) === '*' && strpos($message[2], rtrim($category, '*')) === 0) {
- $matched = true;
- break;
- }
- }
-
- if ($matched) {
- foreach ($this->except as $category) {
- $prefix = rtrim($category, '*');
- if (strpos($message[2], $prefix) === 0 && ($message[2] === $category || $prefix !== $category)) {
- $matched = false;
- break;
- }
- }
- }
-
- if (!$matched) {
- unset($messages[$i]);
- }
- }
- return $messages;
- }
-
- /**
- * Formats a log message.
- * The message structure follows that in [[Logger::messages]].
- * @param array $message the log message to be formatted.
- * @return string the formatted message
- */
- public function formatMessage($message)
- {
- static $levels = array(
- Logger::LEVEL_ERROR => 'error',
- Logger::LEVEL_WARNING => 'warning',
- Logger::LEVEL_INFO => 'info',
- Logger::LEVEL_TRACE => 'trace',
- Logger::LEVEL_PROFILE_BEGIN => 'profile begin',
- Logger::LEVEL_PROFILE_END => 'profile end',
- );
- list($text, $level, $category, $timestamp) = $message;
- $level = isset($levels[$level]) ? $levels[$level] : 'unknown';
- if (!is_string($text)) {
- $text = var_export($text, true);
- }
- $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
- return date('Y/m/d H:i:s', $timestamp) . " [$ip] [$level] [$category] $text\n";
- }
-}
diff --git a/framework/yii/mail/BaseMailer.php b/framework/yii/mail/BaseMailer.php
new file mode 100644
index 0000000..90565c9
--- /dev/null
+++ b/framework/yii/mail/BaseMailer.php
@@ -0,0 +1,300 @@
+
+ * @since 2.0
+ */
+abstract class BaseMailer extends Component implements MailerInterface, ViewContextInterface
+{
+ /**
+ * @var string directory containing view files for this email messages.
+ * This can be specified as an absolute path or path alias.
+ */
+ public $viewPath = '@app/mails';
+ /**
+ * @var string|boolean HTML layout view name. This is the layout used to render HTML mail body.
+ * The property can take the following values:
+ *
+ * - a relative view name: a view file relative to [[viewPath]], e.g., 'layouts/html'.
+ * - a path alias: an absolute view file path specified as a path alias, e.g., '@app/mails/html'.
+ * - a boolean false: the layout is disabled.
+ */
+ public $htmlLayout = 'layouts/html';
+ /**
+ * @var string|boolean text layout view name. This is the layout used to render TEXT mail body.
+ * Please refer to [[htmlLayout]] for possible values that this property can take.
+ */
+ public $textLayout = 'layouts/text';
+ /**
+ * @var array the configuration that should be applied to any newly created
+ * email message instance by [[createMessage()]] or [[compose()]]. Any valid property defined
+ * by [[MessageInterface]] can be configured, such as `from`, `to`, `subject`, `textBody`, `htmlBody`, etc.
+ *
+ * For example:
+ *
+ * ~~~
+ * [
+ * 'charset' => 'UTF-8',
+ * 'from' => 'noreply@mydomain.com',
+ * 'bcc' => 'developer@mydomain.com',
+ * ]
+ * ~~~
+ */
+ public $messageConfig = [];
+ /**
+ * @var string the default class name of the new message instances created by [[createMessage()]]
+ */
+ public $messageClass = 'yii\mail\BaseMessage';
+ /**
+ * @var boolean whether to save email messages as files under [[fileTransportPath]] instead of sending them
+ * to the actual recipients. This is usually used during development for debugging purpose.
+ * @see fileTransportPath
+ */
+ public $useFileTransport = false;
+ /**
+ * @var string the directory where the email messages are saved when [[useFileTransport]] is true.
+ */
+ public $fileTransportPath = '@runtime/mail';
+ /**
+ * @var callback a PHP callback that will be called by [[send()]] when [[useFileTransport]] is true.
+ * The callback should return a file name which will be used to save the email message.
+ * If not set, the file name will be generated based on the current timestamp.
+ *
+ * The signature of the callback is:
+ *
+ * ~~~
+ * function ($mailer, $message)
+ * ~~~
+ */
+ public $fileTransportCallback;
+
+ /**
+ * @var \yii\base\View|array view instance or its array configuration.
+ */
+ private $_view = [];
+
+ /**
+ * @param array|View $view view instance or its array configuration that will be used to
+ * render message bodies.
+ * @throws InvalidConfigException on invalid argument.
+ */
+ public function setView($view)
+ {
+ if (!is_array($view) && !is_object($view)) {
+ throw new InvalidConfigException('"' . get_class($this) . '::view" should be either object or configuration array, "' . gettype($view) . '" given.');
+ }
+ $this->_view = $view;
+ }
+
+ /**
+ * @return View view instance.
+ */
+ public function getView()
+ {
+ if (!is_object($this->_view)) {
+ $this->_view = $this->createView($this->_view);
+ }
+ return $this->_view;
+ }
+
+ /**
+ * Creates view instance from given configuration.
+ * @param array $config view configuration.
+ * @return View view instance.
+ */
+ protected function createView(array $config)
+ {
+ if (!array_key_exists('class', $config)) {
+ $config['class'] = View::className();
+ }
+ return Yii::createObject($config);
+ }
+
+ /**
+ * Creates a new message instance and optionally composes its body content via view rendering.
+ *
+ * @param string|array $view the view to be used for rendering the message body. This can be:
+ *
+ * - a string, which represents the view name or path alias for rendering the HTML body of the email.
+ * In this case, the text body will be generated by applying `strip_tags()` to the HTML body.
+ * - an array with 'html' and/or 'text' elements. The 'html' element refers to the view name or path alias
+ * for rendering the HTML body, while 'text' element is for rendering the text body. For example,
+ * `['html' => 'contact-html', 'text' => 'contact-text']`.
+ * - null, meaning the message instance will be returned without body content.
+ *
+ * The view to be rendered can be specified in one of the following formats:
+ *
+ * - path alias (e.g. "@app/mails/contact");
+ * - a relative view name (e.g. "contact"): the actual view file will be resolved by [[findViewFile()]]
+ *
+ * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
+ * @return MessageInterface message instance.
+ */
+ public function compose($view = null, array $params = [])
+ {
+ $message = $this->createMessage();
+ if ($view !== null) {
+ $params['message'] = $message;
+ if (is_array($view)) {
+ if (isset($view['html'])) {
+ $html = $this->render($view['html'], $params, $this->htmlLayout);
+ }
+ if (isset($view['text'])) {
+ $text = $this->render($view['text'], $params, $this->textLayout);
+ }
+ } else {
+ $html = $this->render($view, $params, $this->htmlLayout);
+ }
+ if (isset($html)) {
+ $message->setHtmlBody($html);
+ }
+ if (isset($text)) {
+ $message->setTextBody($text);
+ } elseif (isset($html)) {
+ $message->setTextBody(strip_tags($html));
+ }
+ }
+ return $message;
+ }
+
+ /**
+ * Creates a new message instance.
+ * The newly created instance will be initialized with the configuration specified by [[messageConfig]].
+ * If the configuration does not specify a 'class', the [[messageClass]] will be used as the class
+ * of the new message instance.
+ * @return MessageInterface message instance.
+ */
+ protected function createMessage()
+ {
+ $config = $this->messageConfig;
+ if (!array_key_exists('class', $config)) {
+ $config['class'] = $this->messageClass;
+ }
+ return Yii::createObject($config);
+ }
+
+ /**
+ * Sends the given email message.
+ * This method will log a message about the email being sent.
+ * If [[useFileTransport]] is true, it will save the email as a file under [[fileTransportPath]].
+ * Otherwise, it will call [[sendMessage()]] to send the email to its recipient(s).
+ * Child classes should implement [[sendMessage()]] with the actual email sending logic.
+ * @param MessageInterface $message email message instance to be sent
+ * @return boolean whether the message has been sent successfully
+ */
+ public function send($message)
+ {
+ $address = $message->getTo();
+ if (is_array($address)) {
+ $address = implode(', ', array_keys($address));
+ }
+ Yii::info('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__);
+
+ if ($this->useFileTransport) {
+ return $this->saveMessage($message);
+ } else {
+ return $this->sendMessage($message);
+ }
+ }
+
+ /**
+ * Sends multiple messages at once.
+ *
+ * The default implementation simply calls [[send()]] multiple times.
+ * Child classes may override this method to implement more efficient way of
+ * sending multiple messages.
+ *
+ * @param array $messages list of email messages, which should be sent.
+ * @return integer number of messages that are successfully sent.
+ */
+ public function sendMultiple(array $messages)
+ {
+ $successCount = 0;
+ foreach ($messages as $message) {
+ if ($this->send($message)) {
+ $successCount++;
+ }
+ }
+ return $successCount;
+ }
+
+ /**
+ * Renders the specified view with optional parameters and layout.
+ * The view will be rendered using the [[view]] component.
+ * @param string $view the view name or the path alias of the view file.
+ * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
+ * @param string|boolean $layout layout view name or path alias. If false, no layout will be applied.
+ * @return string the rendering result.
+ */
+ public function render($view, $params = [], $layout = false)
+ {
+ $output = $this->getView()->render($view, $params, $this);
+ if ($layout !== false) {
+ return $this->getView()->render($layout, ['content' => $output], $this);
+ } else {
+ return $output;
+ }
+ }
+
+ /**
+ * Sends the specified message.
+ * This method should be implemented by child classes with the actual email sending logic.
+ * @param MessageInterface $message the message to be sent
+ * @return boolean whether the message is sent successfully
+ */
+ abstract protected function sendMessage($message);
+
+ /**
+ * Saves the message as a file under [[fileTransportPath]].
+ * @param MessageInterface $message
+ * @return boolean whether the message is saved successfully
+ */
+ protected function saveMessage($message)
+ {
+ $path = Yii::getAlias($this->fileTransportPath);
+ if (!is_dir(($path))) {
+ mkdir($path, 0777, true);
+ }
+ if ($this->fileTransportCallback !== null) {
+ $file = $path . '/' . call_user_func($this->fileTransportCallback, $this, $message);
+ } else {
+ $time = microtime(true);
+ $file = $path . '/' . date('Ymd-His-', $time) . sprintf('%04d', (int)(($time - (int)$time) * 10000)) . '-' . sprintf('%04d', mt_rand(0, 10000)) . '.eml';
+ }
+ file_put_contents($file, $message->toString());
+ return true;
+ }
+
+ /**
+ * Finds the view file corresponding to the specified relative view name.
+ * This method will return the view file by prefixing the view name with [[viewPath]].
+ * @param string $view a relative view name. The name does NOT start with a slash.
+ * @return string the view file path. Note that the file may not exist.
+ */
+ public function findViewFile($view)
+ {
+ return Yii::getAlias($this->viewPath) . DIRECTORY_SEPARATOR . $view;
+ }
+}
diff --git a/framework/yii/mail/BaseMessage.php b/framework/yii/mail/BaseMessage.php
new file mode 100644
index 0000000..d2594fc
--- /dev/null
+++ b/framework/yii/mail/BaseMessage.php
@@ -0,0 +1,52 @@
+
+ * @since 2.0
+ */
+abstract class BaseMessage extends Object implements MessageInterface
+{
+ /**
+ * @inheritdoc
+ */
+ public function send(MailerInterface $mailer = null)
+ {
+ if ($mailer === null) {
+ $mailer = Yii::$app->getMail();
+ }
+ return $mailer->send($this);
+ }
+
+ /**
+ * PHP magic method that returns the string representation of this object.
+ * @return string the string representation of this object.
+ */
+ public function __toString()
+ {
+ // __toString cannot throw exception
+ // use trigger_error to bypass this limitation
+ try {
+ return $this->toString();
+ } catch (\Exception $e) {
+ trigger_error($e->getMessage());
+ return '';
+ }
+ }
+}
diff --git a/framework/yii/mail/MailerInterface.php b/framework/yii/mail/MailerInterface.php
new file mode 100644
index 0000000..5ee9ccd
--- /dev/null
+++ b/framework/yii/mail/MailerInterface.php
@@ -0,0 +1,64 @@
+mail->compose('contact/html', ['contactForm' => $form])
+ * ->setFrom('from@domain.com')
+ * ->setTo($form->email)
+ * ->setSubject($form->subject)
+ * ->send();
+ * ~~~
+ *
+ * @see MessageInterface
+ *
+ * @author Paul Klimov
+ * @since 2.0
+ */
+interface MailerInterface
+{
+ /**
+ * Creates a new message instance and optionally composes its body content via view rendering.
+ *
+ * @param string|array $view the view to be used for rendering the message body. This can be:
+ *
+ * - a string, which represents the view name or path alias for rendering the HTML body of the email.
+ * In this case, the text body will be generated by applying `strip_tags()` to the HTML body.
+ * - an array with 'html' and/or 'text' elements. The 'html' element refers to the view name or path alias
+ * for rendering the HTML body, while 'text' element is for rendering the text body. For example,
+ * `['html' => 'contact-html', 'text' => 'contact-text']`.
+ * - null, meaning the message instance will be returned without body content.
+ *
+ * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
+ * @return MessageInterface message instance.
+ */
+ public function compose($view = null, array $params = []);
+
+ /**
+ * Sends the given email message.
+ * @param MessageInterface $message email message instance to be sent
+ * @return boolean whether the message has been sent successfully
+ */
+ public function send($message);
+
+ /**
+ * Sends multiple messages at once.
+ *
+ * This method may be implemented by some mailers which support more efficient way of sending multiple messages in the same batch.
+ *
+ * @param array $messages list of email messages, which should be sent.
+ * @return integer number of messages that are successfully sent.
+ */
+ public function sendMultiple(array $messages);
+}
diff --git a/framework/yii/mail/MessageInterface.php b/framework/yii/mail/MessageInterface.php
new file mode 100644
index 0000000..94cfd18
--- /dev/null
+++ b/framework/yii/mail/MessageInterface.php
@@ -0,0 +1,218 @@
+mail->compose()
+ * ->setFrom('from@domain.com')
+ * ->setTo($form->email)
+ * ->setSubject($form->subject)
+ * ->setTextBody('Plain text content')
+ * ->setHtmlBody('HTML content ')
+ * ->send();
+ * ~~~
+ *
+ * @see MailerInterface
+ *
+ * @author Paul Klimov
+ * @since 2.0
+ */
+interface MessageInterface
+{
+ /**
+ * Returns the character set of this message.
+ * @return string the character set of this message.
+ */
+ public function getCharset();
+
+ /**
+ * Sets the character set of this message.
+ * @param string $charset character set name.
+ * @return static self reference.
+ */
+ public function setCharset($charset);
+
+ /**
+ * Returns the message sender.
+ * @return string the sender
+ */
+ public function getFrom();
+
+ /**
+ * Sets the message sender.
+ * @param string|array $from sender email address.
+ * You may pass an array of addresses if this message is from multiple people.
+ * You may also specify sender name in addition to email address using format:
+ * `[email => name]`.
+ * @return static self reference.
+ */
+ public function setFrom($from);
+
+ /**
+ * Returns the message recipient(s).
+ * @return array the message recipients
+ */
+ public function getTo();
+
+ /**
+ * Sets the message recipient(s).
+ * @param string|array $to receiver email address.
+ * You may pass an array of addresses if multiple recipients should receive this message.
+ * You may also specify receiver name in addition to email address using format:
+ * `[email => name]`.
+ * @return static self reference.
+ */
+ public function setTo($to);
+
+ /**
+ * Returns the reply-to address of this message.
+ * @return string the reply-to address of this message.
+ */
+ public function getReplyTo();
+
+ /**
+ * Sets the reply-to address of this message.
+ * @param string|array $replyTo the reply-to address.
+ * You may pass an array of addresses if this message should be replied to multiple people.
+ * You may also specify reply-to name in addition to email address using format:
+ * `[email => name]`.
+ * @return static self reference.
+ */
+ public function setReplyTo($replyTo);
+
+ /**
+ * Returns the Cc (additional copy receiver) addresses of this message.
+ * @return array the Cc (additional copy receiver) addresses of this message.
+ */
+ public function getCc();
+
+ /**
+ * Sets the Cc (additional copy receiver) addresses of this message.
+ * @param string|array $cc copy receiver email address.
+ * You may pass an array of addresses if multiple recipients should receive this message.
+ * You may also specify receiver name in addition to email address using format:
+ * `[email => name]`.
+ * @return static self reference.
+ */
+ public function setCc($cc);
+
+ /**
+ * Returns the Bcc (hidden copy receiver) addresses of this message.
+ * @return array the Bcc (hidden copy receiver) addresses of this message.
+ */
+ public function getBcc();
+
+ /**
+ * Sets the Bcc (hidden copy receiver) addresses of this message.
+ * @param string|array $bcc hidden copy receiver email address.
+ * You may pass an array of addresses if multiple recipients should receive this message.
+ * You may also specify receiver name in addition to email address using format:
+ * `[email => name]`.
+ * @return static self reference.
+ */
+ public function setBcc($bcc);
+
+ /**
+ * Returns the message subject.
+ * @return string the message subject
+ */
+ public function getSubject();
+
+ /**
+ * Sets the message subject.
+ * @param string $subject message subject
+ * @return static self reference.
+ */
+ public function setSubject($subject);
+
+ /**
+ * Sets message plain text content.
+ * @param string $text message plain text content.
+ * @return static self reference.
+ */
+ public function setTextBody($text);
+
+ /**
+ * Sets message HTML content.
+ * @param string $html message HTML content.
+ * @return static self reference.
+ */
+ public function setHtmlBody($html);
+
+ /**
+ * Attaches existing file to the email message.
+ * @param string $fileName full file name
+ * @param array $options options for embed file. Valid options are:
+ *
+ * - fileName: name, which should be used to attach file.
+ * - contentType: attached file MIME type.
+ *
+ * @return static self reference.
+ */
+ public function attach($fileName, array $options = []);
+
+ /**
+ * Attach specified content as file for the email message.
+ * @param string $content attachment file content.
+ * @param array $options options for embed file. Valid options are:
+ *
+ * - fileName: name, which should be used to attach file.
+ * - contentType: attached file MIME type.
+ *
+ * @return static self reference.
+ */
+ public function attachContent($content, array $options = []);
+
+ /**
+ * Attach a file and return it's CID source.
+ * This method should be used when embedding images or other data in a message.
+ * @param string $fileName file name.
+ * @param array $options options for embed file. Valid options are:
+ *
+ * - fileName: name, which should be used to attach file.
+ * - contentType: attached file MIME type.
+ *
+ * @return string attachment CID.
+ */
+ public function embed($fileName, array $options = []);
+
+ /**
+ * Attach a content as file and return it's CID source.
+ * This method should be used when embedding images or other data in a message.
+ * @param string $content attachment file content.
+ * @param array $options options for embed file. Valid options are:
+ *
+ * - fileName: name, which should be used to attach file.
+ * - contentType: attached file MIME type.
+ *
+ * @return string attachment CID.
+ */
+ public function embedContent($content, array $options = []);
+
+ /**
+ * Sends this email message.
+ * @param MailerInterface $mailer the mailer that should be used to send this message.
+ * If null, the "mail" application component will be used instead.
+ * @return boolean whether this message is sent successfully.
+ */
+ public function send(MailerInterface $mailer = null);
+
+ /**
+ * Returns string representation of this message.
+ * @return string the string representation of this message.
+ */
+ public function toString();
+}
diff --git a/framework/yii/messages/config.php b/framework/yii/messages/config.php
new file mode 100644
index 0000000..b707abb
--- /dev/null
+++ b/framework/yii/messages/config.php
@@ -0,0 +1,45 @@
+ __DIR__ . '/..',
+ // string, required, root directory containing message translations.
+ 'messagePath' => __DIR__,
+ // array, required, list of language codes that the extracted messages
+ // should be translated to. For example, ['zh-CN', 'de'].
+ 'languages' => ['de'],
+ // string, the name of the function for translating messages.
+ // Defaults to 'Yii::t'. This is used as a mark to find the messages to be
+ // translated. You may use a string for single function name or an array for
+ // multiple function names.
+ 'translator' => 'Yii::t',
+ // boolean, whether to sort messages by keys when merging new messages
+ // with the existing ones. Defaults to false, which means the new (untranslated)
+ // messages will be separated from the old (translated) ones.
+ 'sort' => false,
+ // boolean, whether the message file should be overwritten with the merged messages
+ 'overwrite' => true,
+ // boolean, whether to remove messages that no longer appear in the source code.
+ // Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks.
+ 'removeUnused' => false,
+ // array, list of patterns that specify which files/directories should be processed.
+ // If empty or not set, all files/directories will be processed.
+ // A path matches a pattern if it contains the pattern string at its end. For example,
+ // '/a/b' will match all files and directories ending with '/a/b';
+ // and the '.svn' will match all files and directories whose name ends with '.svn'.
+ // Note, the '/' characters in a pattern matches both '/' and '\'.
+ // If a file/directory matches both a pattern in "only" and "except", it will NOT be processed.
+ 'only' => ['.php'],
+ // array, list of patterns that specify which files/directories should NOT be processed.
+ // If empty or not set, all files/directories will be processed.
+ // Please refer to "only" for details about the patterns.
+ 'except' => [
+ '.svn',
+ '.git',
+ '.gitignore',
+ '.gitkeep',
+ '.hgignore',
+ '.hgkeep',
+ '/messages',
+ ],
+];
diff --git a/framework/yii/mutex/DbMutex.php b/framework/yii/mutex/DbMutex.php
new file mode 100644
index 0000000..3699c36
--- /dev/null
+++ b/framework/yii/mutex/DbMutex.php
@@ -0,0 +1,41 @@
+
+ * @since 2.0
+ */
+abstract class DbMutex extends Mutex
+{
+ /**
+ * @var Connection|string the DB connection object or the application component ID of the DB connection.
+ * After the Mutex object is created, if you want to change this property, you should only assign
+ * it with a DB connection object.
+ */
+ public $db = 'db';
+
+ /**
+ * Initializes generic database table based mutex implementation.
+ * @throws InvalidConfigException if [[db]] is invalid.
+ */
+ public function init()
+ {
+ parent::init();
+ if (is_string($this->db)) {
+ $this->db = Yii::$app->getComponent($this->db);
+ }
+ if (!$this->db instanceof Connection) {
+ throw new InvalidConfigException('Mutex::db must be either a DB connection instance or the application component ID of a DB connection.');
+ }
+ }
+}
diff --git a/framework/yii/mutex/FileMutex.php b/framework/yii/mutex/FileMutex.php
new file mode 100644
index 0000000..fd5cb00
--- /dev/null
+++ b/framework/yii/mutex/FileMutex.php
@@ -0,0 +1,104 @@
+
+ * @since 2.0
+ */
+class FileMutex extends Mutex
+{
+ /**
+ * @var string the directory to store mutex files. You may use path alias here.
+ * Defaults to the "mutex" subdirectory under the application runtime path.
+ */
+ public $mutexPath = '@runtime/mutex';
+ /**
+ * @var integer the permission to be set for newly created mutex files.
+ * This value will be used by PHP chmod() function. No umask will be applied.
+ * If not set, the permission will be determined by the current environment.
+ */
+ public $fileMode;
+ /**
+ * @var integer the permission to be set for newly created directories.
+ * This value will be used by PHP chmod() function. No umask will be applied.
+ * Defaults to 0775, meaning the directory is read-writable by owner and group,
+ * but read-only for other users.
+ */
+ public $dirMode = 0775;
+ /**
+ * @var resource[] stores all opened lock files. Keys are lock names and values are file handles.
+ */
+ private $_files = [];
+
+
+ /**
+ * Initializes mutex component implementation dedicated for UNIX, GNU/Linux, Mac OS X, and other UNIX-like
+ * operating systems.
+ * @throws InvalidConfigException
+ */
+ public function init()
+ {
+ if (stripos(php_uname('s'), 'win') === 0) {
+ throw new InvalidConfigException('FileMutex does not have MS Windows operating system support.');
+ }
+ $this->mutexPath = Yii::getAlias($this->mutexPath);
+ if (!is_dir($this->mutexPath)) {
+ FileHelper::createDirectory($this->mutexPath, $this->dirMode, true);
+ }
+ }
+
+ /**
+ * Acquires lock by given name.
+ * @param string $name of the lock to be acquired.
+ * @param integer $timeout to wait for lock to become released.
+ * @return boolean acquiring result.
+ */
+ protected function acquireLock($name, $timeout = 0)
+ {
+ $fileName = $this->mutexPath . '/' . md5($name) . '.lock';
+ $file = fopen($fileName, 'w+');
+ if ($file === false) {
+ return false;
+ }
+ if ($this->fileMode !== null) {
+ @chmod($fileName, $this->fileMode);
+ }
+ $waitTime = 0;
+ while (!flock($file, LOCK_EX | LOCK_NB)) {
+ $waitTime++;
+ if ($waitTime > $timeout) {
+ fclose($file);
+ return false;
+ }
+ sleep(1);
+ }
+ $this->_files[$name] = $file;
+ return true;
+ }
+
+ /**
+ * Releases lock by given name.
+ * @param string $name of the lock to be released.
+ * @return boolean release result.
+ */
+ protected function releaseLock($name)
+ {
+ if (!isset($this->_files[$name]) || !flock($this->_files[$name], LOCK_UN)) {
+ return false;
+ } else {
+ fclose($this->_files[$name]);
+ unset($this->_files[$name]);
+ return true;
+ }
+ }
+}
diff --git a/framework/yii/mutex/Mutex.php b/framework/yii/mutex/Mutex.php
new file mode 100644
index 0000000..611e725
--- /dev/null
+++ b/framework/yii/mutex/Mutex.php
@@ -0,0 +1,95 @@
+
+ * @since 2.0
+ */
+abstract class Mutex extends Component
+{
+ /**
+ * @var boolean whether all locks acquired in this process (i.e. local locks) must be released automagically
+ * before finishing script execution. Defaults to true. Setting this property to true means that all locks
+ * acquire in this process must be released in any case (regardless any kind of errors or exceptions).
+ */
+ public $autoRelease = true;
+ /**
+ * @var string[] names of the locks acquired in the current PHP process.
+ */
+ private $_locks = [];
+
+
+ /**
+ * Initializes the mutex component.
+ */
+ public function init()
+ {
+ if ($this->autoRelease) {
+ $locks = &$this->_locks;
+ register_shutdown_function(function () use (&$locks) {
+ foreach ($locks as $lock) {
+ $this->release($lock);
+ }
+ });
+ }
+ }
+
+ /**
+ * Acquires lock by given name.
+ * @param string $name of the lock to be acquired. Must be unique.
+ * @param integer $timeout to wait for lock to be released. Defaults to zero meaning that method will return
+ * false immediately in case lock was already acquired.
+ * @return boolean lock acquiring result.
+ */
+ public function acquire($name, $timeout = 0)
+ {
+ if ($this->acquireLock($name, $timeout)) {
+ $this->_locks[] = $name;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Release acquired lock. This method will return false in case named lock was not found.
+ * @param string $name of the lock to be released. This lock must be already created.
+ * @return boolean lock release result: false in case named lock was not found..
+ */
+ public function release($name)
+ {
+ if ($this->releaseLock($name)) {
+ $index = array_search($name, $this->_locks);
+ if ($index !== false) {
+ unset($this->_locks[$index]);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This method should be extended by concrete mutex implementations. Acquires lock by given name.
+ * @param string $name of the lock to be acquired.
+ * @param integer $timeout to wait for lock to become released.
+ * @return boolean acquiring result.
+ */
+ abstract protected function acquireLock($name, $timeout = 0);
+
+ /**
+ * This method should be extended by concrete mutex implementations. Releases lock by given name.
+ * @param string $name of the lock to be released.
+ * @return boolean release result.
+ */
+ abstract protected function releaseLock($name);
+}
diff --git a/framework/yii/mutex/MysqlMutex.php b/framework/yii/mutex/MysqlMutex.php
new file mode 100644
index 0000000..af05b9c
--- /dev/null
+++ b/framework/yii/mutex/MysqlMutex.php
@@ -0,0 +1,57 @@
+
+ * @since 2.0
+ */
+class MysqlMutex extends Mutex
+{
+ /**
+ * Initializes MySQL specific mutex component implementation.
+ * @throws InvalidConfigException if [[db]] is not MySQL connection.
+ */
+ public function init()
+ {
+ parent::init();
+ if ($this->db->driverName !== 'mysql') {
+ throw new InvalidConfigException('In order to use MysqlMutex connection must be configured to use MySQL database.');
+ }
+ }
+
+ /**
+ * Acquires lock by given name.
+ * @param string $name of the lock to be acquired.
+ * @param integer $timeout to wait for lock to become released.
+ * @return boolean acquiring result.
+ * @see http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock
+ */
+ protected function acquireLock($name, $timeout = 0)
+ {
+ return (boolean)$this->db
+ ->createCommand('SELECT GET_LOCK(:name, :timeout)', [':name' => $name, ':timeout' => $timeout])
+ ->queryScalar();
+ }
+
+ /**
+ * Releases lock by given name.
+ * @param string $name of the lock to be released.
+ * @return boolean release result.
+ * @see http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock
+ */
+ protected function releaseLock($name)
+ {
+ return (boolean)$this->db
+ ->createCommand('SELECT RELEASE_LOCK(:name)', [':name' => $name])
+ ->queryScalar();
+ }
+}
diff --git a/framework/yii/rbac/DbManager.php b/framework/yii/rbac/DbManager.php
index 8d3bea2..e9e1730 100644
--- a/framework/yii/rbac/DbManager.php
+++ b/framework/yii/rbac/DbManager.php
@@ -24,6 +24,8 @@ use yii\base\InvalidParamException;
* the three tables used to store the authorization data by setting [[itemTable]],
* [[itemChildTable]] and [[assignmentTable]].
*
+ * @property Item[] $items The authorization items of the specific type. This property is read-only.
+ *
* @author Qiang Xue
* @author Alexander Kochetov
* @since 2.0
@@ -77,7 +79,7 @@ class DbManager extends Manager
* which holds the value of `$userId`.
* @return boolean whether the operations can be performed by the user.
*/
- public function checkAccess($userId, $itemName, $params = array())
+ public function checkAccess($userId, $itemName, $params = [])
{
$assignments = $this->getAssignments($userId);
return $this->checkAccessRecursive($userId, $itemName, $params, $assignments);
@@ -115,9 +117,9 @@ class DbManager extends Manager
}
}
$query = new Query;
- $parents = $query->select(array('parent'))
+ $parents = $query->select(['parent'])
->from($this->itemChildTable)
- ->where(array('child' => $itemName))
+ ->where(['child' => $itemName])
->createCommand($this->db)
->queryColumn();
foreach ($parents as $parent) {
@@ -144,7 +146,7 @@ class DbManager extends Manager
}
$query = new Query;
$rows = $query->from($this->itemTable)
- ->where(array('or', 'name=:name1', 'name=:name2'), array(':name1' => $itemName, ':name2' => $childName))
+ ->where(['or', 'name=:name1', 'name=:name2'], [':name1' => $itemName, ':name2' => $childName])
->createCommand($this->db)
->queryAll();
if (count($rows) == 2) {
@@ -160,7 +162,7 @@ class DbManager extends Manager
throw new InvalidCallException("Cannot add '$childName' as a child of '$itemName'. A loop has been detected.");
}
$this->db->createCommand()
- ->insert($this->itemChildTable, array('parent' => $itemName, 'child' => $childName))
+ ->insert($this->itemChildTable, ['parent' => $itemName, 'child' => $childName])
->execute();
return true;
} else {
@@ -178,7 +180,7 @@ class DbManager extends Manager
public function removeItemChild($itemName, $childName)
{
return $this->db->createCommand()
- ->delete($this->itemChildTable, array('parent' => $itemName, 'child' => $childName))
+ ->delete($this->itemChildTable, ['parent' => $itemName, 'child' => $childName])
->execute() > 0;
}
@@ -191,9 +193,9 @@ class DbManager extends Manager
public function hasItemChild($itemName, $childName)
{
$query = new Query;
- return $query->select(array('parent'))
+ return $query->select(['parent'])
->from($this->itemChildTable)
- ->where(array('parent' => $itemName, 'child' => $childName))
+ ->where(['parent' => $itemName, 'child' => $childName])
->createCommand($this->db)
->queryScalar() !== false;
}
@@ -207,24 +209,24 @@ class DbManager extends Manager
public function getItemChildren($names)
{
$query = new Query;
- $rows = $query->select(array('name', 'type', 'description', 'biz_rule', 'data'))
- ->from(array($this->itemTable, $this->itemChildTable))
- ->where(array('parent' => $names, 'name' => new Expression('child')))
+ $rows = $query->select(['name', 'type', 'description', 'biz_rule', 'data'])
+ ->from([$this->itemTable, $this->itemChildTable])
+ ->where(['parent' => $names, 'name' => new Expression('child')])
->createCommand($this->db)
->queryAll();
- $children = array();
+ $children = [];
foreach ($rows as $row) {
if (($data = @unserialize($row['data'])) === false) {
$data = null;
}
- $children[$row['name']] = new Item(array(
+ $children[$row['name']] = new Item([
'manager' => $this,
'name' => $row['name'],
'type' => $row['type'],
'description' => $row['description'],
'bizRule' => $row['biz_rule'],
'data' => $data,
- ));
+ ]);
}
return $children;
}
@@ -245,20 +247,20 @@ class DbManager extends Manager
throw new InvalidParamException("The item '$itemName' does not exist.");
}
$this->db->createCommand()
- ->insert($this->assignmentTable, array(
+ ->insert($this->assignmentTable, [
'user_id' => $userId,
'item_name' => $itemName,
'biz_rule' => $bizRule,
'data' => serialize($data),
- ))
+ ])
->execute();
- return new Assignment(array(
+ return new Assignment([
'manager' => $this,
'userId' => $userId,
'itemName' => $itemName,
'bizRule' => $bizRule,
'data' => $data,
- ));
+ ]);
}
/**
@@ -270,11 +272,23 @@ class DbManager extends Manager
public function revoke($userId, $itemName)
{
return $this->db->createCommand()
- ->delete($this->assignmentTable, array('user_id' => $userId, 'item_name' => $itemName))
+ ->delete($this->assignmentTable, ['user_id' => $userId, 'item_name' => $itemName])
->execute() > 0;
}
/**
+ * Revokes all authorization assignments from a user.
+ * @param mixed $userId the user ID (see [[User::id]])
+ * @return boolean whether removal is successful
+ */
+ public function revokeAll($userId)
+ {
+ return $this->db->createCommand()
+ ->delete($this->assignmentTable, ['user_id' => $userId])
+ ->execute() > 0;
+ }
+
+ /**
* Returns a value indicating whether the item has been assigned to the user.
* @param mixed $userId the user ID (see [[User::id]])
* @param string $itemName the item name
@@ -283,9 +297,9 @@ class DbManager extends Manager
public function isAssigned($userId, $itemName)
{
$query = new Query;
- return $query->select(array('item_name'))
+ return $query->select(['item_name'])
->from($this->assignmentTable)
- ->where(array('user_id' => $userId, 'item_name' => $itemName))
+ ->where(['user_id' => $userId, 'item_name' => $itemName])
->createCommand($this->db)
->queryScalar() !== false;
}
@@ -301,20 +315,20 @@ class DbManager extends Manager
{
$query = new Query;
$row = $query->from($this->assignmentTable)
- ->where(array('user_id' => $userId, 'item_name' => $itemName))
+ ->where(['user_id' => $userId, 'item_name' => $itemName])
->createCommand($this->db)
- ->queryRow();
+ ->queryOne();
if ($row !== false) {
if (($data = @unserialize($row['data'])) === false) {
$data = null;
}
- return new Assignment(array(
+ return new Assignment([
'manager' => $this,
'userId' => $row['user_id'],
'itemName' => $row['item_name'],
'bizRule' => $row['biz_rule'],
'data' => $data,
- ));
+ ]);
} else {
return null;
}
@@ -330,21 +344,21 @@ class DbManager extends Manager
{
$query = new Query;
$rows = $query->from($this->assignmentTable)
- ->where(array('user_id' => $userId))
+ ->where(['user_id' => $userId])
->createCommand($this->db)
->queryAll();
- $assignments = array();
+ $assignments = [];
foreach ($rows as $row) {
if (($data = @unserialize($row['data'])) === false) {
$data = null;
}
- $assignments[$row['item_name']] = new Assignment(array(
+ $assignments[$row['item_name']] = new Assignment([
'manager' => $this,
'userId' => $row['user_id'],
'itemName' => $row['item_name'],
'bizRule' => $row['biz_rule'],
'data' => $data,
- ));
+ ]);
}
return $assignments;
}
@@ -356,13 +370,13 @@ class DbManager extends Manager
public function saveAssignment($assignment)
{
$this->db->createCommand()
- ->update($this->assignmentTable, array(
+ ->update($this->assignmentTable, [
'biz_rule' => $assignment->bizRule,
'data' => serialize($assignment->data),
- ), array(
+ ], [
'user_id' => $assignment->userId,
'item_name' => $assignment->itemName,
- ))
+ ])
->execute();
}
@@ -382,32 +396,32 @@ class DbManager extends Manager
->createCommand($this->db);
} elseif ($userId === null) {
$command = $query->from($this->itemTable)
- ->where(array('type' => $type))
+ ->where(['type' => $type])
->createCommand($this->db);
} elseif ($type === null) {
- $command = $query->select(array('name', 'type', 'description', 't1.biz_rule', 't1.data'))
- ->from(array($this->itemTable . ' t1', $this->assignmentTable . ' t2'))
- ->where(array('user_id' => $userId, 'name' => new Expression('item_name')))
+ $command = $query->select(['name', 'type', 'description', 't1.biz_rule', 't1.data'])
+ ->from([$this->itemTable . ' t1', $this->assignmentTable . ' t2'])
+ ->where(['user_id' => $userId, 'name' => new Expression('item_name')])
->createCommand($this->db);
} else {
- $command = $query->select('name', 'type', 'description', 't1.biz_rule', 't1.data')
- ->from(array($this->itemTable . ' t1', $this->assignmentTable . ' t2'))
- ->where(array('user_id' => $userId, 'type' => $type, 'name' => new Expression('item_name')))
+ $command = $query->select(['name', 'type', 'description', 't1.biz_rule', 't1.data'])
+ ->from([$this->itemTable . ' t1', $this->assignmentTable . ' t2'])
+ ->where(['user_id' => $userId, 'type' => $type, 'name' => new Expression('item_name')])
->createCommand($this->db);
}
- $items = array();
+ $items = [];
foreach ($command->queryAll() as $row) {
if (($data = @unserialize($row['data'])) === false) {
$data = null;
}
- $items[$row['name']] = new Item(array(
+ $items[$row['name']] = new Item([
'manager' => $this,
'name' => $row['name'],
'type' => $row['type'],
'description' => $row['description'],
'bizRule' => $row['biz_rule'],
'data' => $data,
- ));
+ ]);
}
return $items;
}
@@ -430,22 +444,22 @@ class DbManager extends Manager
public function createItem($name, $type, $description = '', $bizRule = null, $data = null)
{
$this->db->createCommand()
- ->insert($this->itemTable, array(
+ ->insert($this->itemTable, [
'name' => $name,
'type' => $type,
'description' => $description,
'biz_rule' => $bizRule,
'data' => serialize($data),
- ))
+ ])
->execute();
- return new Item(array(
+ return new Item([
'manager' => $this,
'name' => $name,
'type' => $type,
'description' => $description,
'bizRule' => $bizRule,
'data' => $data,
- ));
+ ]);
}
/**
@@ -457,14 +471,14 @@ class DbManager extends Manager
{
if ($this->usingSqlite()) {
$this->db->createCommand()
- ->delete($this->itemChildTable, array('or', 'parent=:name', 'child=:name'), array(':name' => $name))
+ ->delete($this->itemChildTable, ['or', 'parent=:name', 'child=:name'], [':name' => $name])
->execute();
$this->db->createCommand()
- ->delete($this->assignmentTable, array('item_name' => $name))
+ ->delete($this->assignmentTable, ['item_name' => $name])
->execute();
}
return $this->db->createCommand()
- ->delete($this->itemTable, array('name' => $name))
+ ->delete($this->itemTable, ['name' => $name])
->execute() > 0;
}
@@ -477,22 +491,22 @@ class DbManager extends Manager
{
$query = new Query;
$row = $query->from($this->itemTable)
- ->where(array('name' => $name))
+ ->where(['name' => $name])
->createCommand($this->db)
- ->queryRow();
+ ->queryOne();
if ($row !== false) {
if (($data = @unserialize($row['data'])) === false) {
$data = null;
}
- return new Item(array(
+ return new Item([
'manager' => $this,
'name' => $row['name'],
'type' => $row['type'],
'description' => $row['description'],
'bizRule' => $row['biz_rule'],
'data' => $data,
- ));
+ ]);
} else {
return null;
}
@@ -507,26 +521,26 @@ class DbManager extends Manager
{
if ($this->usingSqlite() && $oldName !== null && $item->getName() !== $oldName) {
$this->db->createCommand()
- ->update($this->itemChildTable, array('parent' => $item->getName()), array('parent' => $oldName))
+ ->update($this->itemChildTable, ['parent' => $item->getName()], ['parent' => $oldName])
->execute();
$this->db->createCommand()
- ->update($this->itemChildTable, array('child' => $item->getName()), array('child' => $oldName))
+ ->update($this->itemChildTable, ['child' => $item->getName()], ['child' => $oldName])
->execute();
$this->db->createCommand()
- ->update($this->assignmentTable, array('item_name' => $item->getName()), array('item_name' => $oldName))
+ ->update($this->assignmentTable, ['item_name' => $item->getName()], ['item_name' => $oldName])
->execute();
}
$this->db->createCommand()
- ->update($this->itemTable, array(
+ ->update($this->itemTable, [
'name' => $item->getName(),
'type' => $item->type,
'description' => $item->description,
'biz_rule' => $item->bizRule,
'data' => serialize($item->data),
- ), array(
+ ], [
'name' => $oldName === null ? $item->getName() : $oldName,
- ))
+ ])
->execute();
}
diff --git a/framework/yii/rbac/Item.php b/framework/yii/rbac/Item.php
index 2504ad5..ae9214e 100644
--- a/framework/yii/rbac/Item.php
+++ b/framework/yii/rbac/Item.php
@@ -18,6 +18,9 @@ use yii\base\Object;
* A user may be assigned one or several authorization items (called [[Assignment]] assignments).
* He can perform an operation only when it is among his assigned items.
*
+ * @property Item[] $children All child items of this item. This property is read-only.
+ * @property string $name The item name.
+ *
* @author Qiang Xue
* @author Alexander Kochetov
* @since 2.0
@@ -61,7 +64,7 @@ class Item extends Object
* @param array $params the parameters to be passed to business rule evaluation
* @return boolean whether the specified item is within the hierarchy starting from this item.
*/
- public function checkAccess($itemName, $params = array())
+ public function checkAccess($itemName, $params = [])
{
Yii::trace('Checking permission: ' . $this->_name, __METHOD__);
if ($this->manager->executeBizRule($this->bizRule, $params, $this->data)) {
diff --git a/framework/yii/rbac/Manager.php b/framework/yii/rbac/Manager.php
index 23ac1a8..a1bf47a 100644
--- a/framework/yii/rbac/Manager.php
+++ b/framework/yii/rbac/Manager.php
@@ -33,9 +33,9 @@ use yii\base\InvalidParamException;
* at appropriate places in the application code to check if the current user
* has the needed permission for an operation.
*
- * @property array $roles Roles (name => Item).
- * @property array $tasks Tasks (name => Item).
- * @property array $operations Operations (name => Item).
+ * @property Item[] $operations Operations (name => AuthItem). This property is read-only.
+ * @property Item[] $roles Roles (name => AuthItem). This property is read-only.
+ * @property Item[] $tasks Tasks (name => AuthItem). This property is read-only.
*
* @author Qiang Xue
* @author Alexander Kochetov
@@ -58,7 +58,7 @@ abstract class Manager extends Component
* And then declare 'authenticated' in this property so that it can be applied to
* every authenticated user.
*/
- public $defaultRoles = array();
+ public $defaultRoles = [];
/**
* Creates a role.
@@ -159,7 +159,7 @@ abstract class Manager extends Component
*/
protected function checkItemChildType($parentType, $childType)
{
- static $types = array('operation', 'task', 'role');
+ static $types = ['operation', 'task', 'role'];
if ($parentType < $childType) {
throw new InvalidParamException("Cannot add an item of type '{$types[$childType]}' to an item of type '{$types[$parentType]}'.");
}
@@ -174,7 +174,7 @@ abstract class Manager extends Component
* with the tasks and roles assigned to the user.
* @return boolean whether the operations can be performed by the user.
*/
- abstract public function checkAccess($userId, $itemName, $params = array());
+ abstract public function checkAccess($userId, $itemName, $params = []);
/**
* Creates an authorization item.
@@ -269,6 +269,12 @@ abstract class Manager extends Component
*/
abstract public function revoke($userId, $itemName);
/**
+ * Revokes all authorization assignments from a user.
+ * @param mixed $userId the user ID (see [[User::id]])
+ * @return boolean whether removal is successful
+ */
+ abstract public function revokeAll($userId);
+ /**
* Returns a value indicating whether the item has been assigned to the user.
* @param mixed $userId the user ID (see [[User::id]])
* @param string $itemName the item name
diff --git a/framework/yii/rbac/PhpManager.php b/framework/yii/rbac/PhpManager.php
index 8ecc75c..3d02e4f 100644
--- a/framework/yii/rbac/PhpManager.php
+++ b/framework/yii/rbac/PhpManager.php
@@ -23,7 +23,7 @@ use yii\base\InvalidParamException;
* (for example, the authorization data for a personal blog system).
* Use [[DbManager]] for more complex authorization data.
*
- * @property array $authItems The authorization items of the specific type.
+ * @property Item[] $items The authorization items of the specific type. This property is read-only.
*
* @author Qiang Xue
* @author Alexander Kochetov
@@ -33,17 +33,16 @@ class PhpManager extends Manager
{
/**
* @var string the path of the PHP script that contains the authorization data.
- * If not set, it will be using 'protected/data/rbac.php' as the data file.
- * Make sure this file is writable by the Web server process if the authorization
- * needs to be changed.
- * @see loadFromFile
- * @see saveToFile
+ * This can be either a file path or a path alias to the file.
+ * Make sure this file is writable by the Web server process if the authorization needs to be changed online.
+ * @see loadFromFile()
+ * @see saveToFile()
*/
- public $authFile;
+ public $authFile = '@app/data/rbac.php';
- private $_items = array(); // itemName => item
- private $_children = array(); // itemName, childName => child
- private $_assignments = array(); // userId, itemName => assignment
+ private $_items = []; // itemName => item
+ private $_children = []; // itemName, childName => child
+ private $_assignments = []; // userId, itemName => assignment
/**
* Initializes the application component.
@@ -53,9 +52,7 @@ class PhpManager extends Manager
public function init()
{
parent::init();
- if ($this->authFile === null) {
- $this->authFile = Yii::getAlias('@app/data/rbac') . '.php';
- }
+ $this->authFile = Yii::getAlias($this->authFile);
$this->load();
}
@@ -69,12 +66,12 @@ class PhpManager extends Manager
* this array, which holds the value of `$userId`.
* @return boolean whether the operations can be performed by the user.
*/
- public function checkAccess($userId, $itemName, $params = array())
+ public function checkAccess($userId, $itemName, $params = [])
{
if (!isset($this->_items[$itemName])) {
return false;
}
- /** @var $item Item */
+ /** @var Item $item */
$item = $this->_items[$itemName];
Yii::trace('Checking permission: ' . $item->getName(), __METHOD__);
if (!isset($params['userId'])) {
@@ -85,7 +82,7 @@ class PhpManager extends Manager
return true;
}
if (isset($this->_assignments[$userId][$itemName])) {
- /** @var $assignment Assignment */
+ /** @var Assignment $assignment */
$assignment = $this->_assignments[$userId][$itemName];
if ($this->executeBizRule($assignment->bizRule, $params, $assignment->data)) {
return true;
@@ -113,9 +110,9 @@ class PhpManager extends Manager
if (!isset($this->_items[$childName], $this->_items[$itemName])) {
throw new Exception("Either '$itemName' or '$childName' does not exist.");
}
- /** @var $child Item */
+ /** @var Item $child */
$child = $this->_items[$childName];
- /** @var $item Item */
+ /** @var Item $item */
$item = $this->_items[$itemName];
$this->checkItemChildType($item->type, $child->type);
if ($this->detectLoop($itemName, $childName)) {
@@ -165,10 +162,10 @@ class PhpManager extends Manager
public function getItemChildren($names)
{
if (is_string($names)) {
- return isset($this->_children[$names]) ? $this->_children[$names] : array();
+ return isset($this->_children[$names]) ? $this->_children[$names] : [];
}
- $children = array();
+ $children = [];
foreach ($names as $name) {
if (isset($this->_children[$name])) {
$children = array_merge($children, $this->_children[$name]);
@@ -194,13 +191,13 @@ class PhpManager extends Manager
} elseif (isset($this->_assignments[$userId][$itemName])) {
throw new InvalidParamException("Authorization item '$itemName' has already been assigned to user '$userId'.");
} else {
- return $this->_assignments[$userId][$itemName] = new Assignment(array(
+ return $this->_assignments[$userId][$itemName] = new Assignment([
'manager' => $this,
'userId' => $userId,
'itemName' => $itemName,
'bizRule' => $bizRule,
'data' => $data,
- ));
+ ]);
}
}
@@ -221,6 +218,22 @@ class PhpManager extends Manager
}
/**
+ * Revokes all authorization assignments from a user.
+ * @param mixed $userId the user ID (see [[User::id]])
+ * @return boolean whether removal is successful
+ */
+ public function revokeAll($userId)
+ {
+ if (isset($this->_assignments[$userId]) && is_array($this->_assignments[$userId])) {
+ foreach ($this->_assignments[$userId] as $itemName => $value)
+ unset($this->_assignments[$userId][$itemName]);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
* Returns a value indicating whether the item has been assigned to the user.
* @param mixed $userId the user ID (see [[User::id]])
* @param string $itemName the item name
@@ -251,7 +264,7 @@ class PhpManager extends Manager
*/
public function getAssignments($userId)
{
- return isset($this->_assignments[$userId]) ? $this->_assignments[$userId] : array();
+ return isset($this->_assignments[$userId]) ? $this->_assignments[$userId] : [];
}
/**
@@ -267,17 +280,17 @@ class PhpManager extends Manager
if ($userId === null && $type === null) {
return $this->_items;
}
- $items = array();
+ $items = [];
if ($userId === null) {
foreach ($this->_items as $name => $item) {
- /** @var $item Item */
+ /** @var Item $item */
if ($item->type == $type) {
$items[$name] = $item;
}
}
} elseif (isset($this->_assignments[$userId])) {
foreach ($this->_assignments[$userId] as $assignment) {
- /** @var $assignment Assignment */
+ /** @var Assignment $assignment */
$name = $assignment->itemName;
if (isset($this->_items[$name]) && ($type === null || $this->_items[$name]->type == $type)) {
$items[$name] = $this->_items[$name];
@@ -307,14 +320,14 @@ class PhpManager extends Manager
if (isset($this->_items[$name])) {
throw new Exception('Unable to add an item whose name is the same as an existing item.');
}
- return $this->_items[$name] = new Item(array(
+ return $this->_items[$name] = new Item([
'manager' => $this,
'name' => $name,
'type' => $type,
'description' => $description,
'bizRule' => $bizRule,
'data' => $data,
- ));
+ ]);
}
/**
@@ -356,8 +369,7 @@ class PhpManager extends Manager
*/
public function saveItem($item, $oldName = null)
{
- if ($oldName !== null && ($newName = $item->getName()) !== $oldName) // name changed
- {
+ if ($oldName !== null && ($newName = $item->getName()) !== $oldName) { // name changed
if (isset($this->_items[$newName])) {
throw new InvalidParamException("Unable to change the item name. The name '$newName' is already used by another item.");
}
@@ -399,18 +411,18 @@ class PhpManager extends Manager
*/
public function save()
{
- $items = array();
+ $items = [];
foreach ($this->_items as $name => $item) {
- /** @var $item Item */
- $items[$name] = array(
+ /** @var Item $item */
+ $items[$name] = [
'type' => $item->type,
'description' => $item->description,
'bizRule' => $item->bizRule,
'data' => $item->data,
- );
+ ];
if (isset($this->_children[$name])) {
foreach ($this->_children[$name] as $child) {
- /** @var $child Item */
+ /** @var Item $child */
$items[$name]['children'][] = $child->getName();
}
}
@@ -418,12 +430,12 @@ class PhpManager extends Manager
foreach ($this->_assignments as $userId => $assignments) {
foreach ($assignments as $name => $assignment) {
- /** @var $assignment Assignment */
+ /** @var Assignment $assignment */
if (isset($items[$name])) {
- $items[$name]['assignments'][$userId] = array(
+ $items[$name]['assignments'][$userId] = [
'bizRule' => $assignment->bizRule,
'data' => $assignment->data,
- );
+ ];
}
}
}
@@ -441,14 +453,14 @@ class PhpManager extends Manager
$items = $this->loadFromFile($this->authFile);
foreach ($items as $name => $item) {
- $this->_items[$name] = new Item(array(
+ $this->_items[$name] = new Item([
'manager' => $this,
'name' => $name,
'type' => $item['type'],
'description' => $item['description'],
'bizRule' => $item['bizRule'],
'data' => $item['data'],
- ));
+ ]);
}
foreach ($items as $name => $item) {
@@ -461,13 +473,13 @@ class PhpManager extends Manager
}
if (isset($item['assignments'])) {
foreach ($item['assignments'] as $userId => $assignment) {
- $this->_assignments[$userId][$name] = new Assignment(array(
+ $this->_assignments[$userId][$name] = new Assignment([
'manager' => $this,
'userId' => $userId,
'itemName' => $name,
'bizRule' => $assignment['bizRule'],
'data' => $assignment['data'],
- ));
+ ]);
}
}
}
@@ -479,8 +491,8 @@ class PhpManager extends Manager
public function clearAll()
{
$this->clearAssignments();
- $this->_children = array();
- $this->_items = array();
+ $this->_children = [];
+ $this->_items = [];
}
/**
@@ -488,7 +500,7 @@ class PhpManager extends Manager
*/
public function clearAssignments()
{
- $this->_assignments = array();
+ $this->_assignments = [];
}
/**
@@ -506,7 +518,7 @@ class PhpManager extends Manager
return false;
}
foreach ($this->_children[$childName] as $child) {
- /** @var $child Item */
+ /** @var Item $child */
if ($this->detectLoop($itemName, $child->getName())) {
return true;
}
@@ -518,14 +530,14 @@ class PhpManager extends Manager
* Loads the authorization data from a PHP script file.
* @param string $file the file path.
* @return array the authorization data
- * @see saveToFile
+ * @see saveToFile()
*/
protected function loadFromFile($file)
{
if (is_file($file)) {
return require($file);
} else {
- return array();
+ return [];
}
}
@@ -533,7 +545,7 @@ class PhpManager extends Manager
* Saves the authorization data to a PHP script file.
* @param array $data the authorization data
* @param string $file the file path.
- * @see loadFromFile
+ * @see loadFromFile()
*/
protected function saveToFile($data, $file)
{
diff --git a/framework/yii/rbac/schema-pgsql.sql b/framework/yii/rbac/schema-pgsql.sql
index 78dbb3d..bf9865f 100644
--- a/framework/yii/rbac/schema-pgsql.sql
+++ b/framework/yii/rbac/schema-pgsql.sql
@@ -20,10 +20,11 @@ create table "tbl_auth_item"
"description" text,
"biz_rule" text,
"data" text,
- primary key ("name"),
- key "type" ("type")
+ primary key ("name")
);
+create index tbl_auth_item_type_idx on "tbl_auth_item" ("type");
+
create table "tbl_auth_item_child"
(
"parent" varchar(64) not null,
diff --git a/framework/yii/requirements/YiiRequirementChecker.php b/framework/yii/requirements/YiiRequirementChecker.php
index 7d5ae42..586cdce 100644
--- a/framework/yii/requirements/YiiRequirementChecker.php
+++ b/framework/yii/requirements/YiiRequirementChecker.php
@@ -63,7 +63,7 @@ class YiiRequirementChecker
* @param array|string $requirements requirements to be checked.
* If an array, it is treated as the set of requirements;
* If a string, it is treated as the path of the file, which contains the requirements;
- * @return YiiRequirementChecker self instance.
+ * @return static self instance.
*/
function check($requirements)
{
@@ -178,6 +178,9 @@ class YiiRequirementChecker
if (empty($extensionVersion)) {
return false;
}
+ if (strncasecmp($extensionVersion, 'PECL-', 5) == 0) {
+ $extensionVersion = substr($extensionVersion, 5);
+ }
return version_compare($extensionVersion, $version, $compare);
}
diff --git a/framework/yii/requirements/requirements.php b/framework/yii/requirements/requirements.php
index 670544d..916d6aa 100644
--- a/framework/yii/requirements/requirements.php
+++ b/framework/yii/requirements/requirements.php
@@ -3,15 +3,15 @@
* These are the Yii core requirements for the [[YiiRequirementChecker]] instance.
* These requirements are mandatory for any Yii application.
*
- * @var $this YiiRequirementChecker
+ * @var YiiRequirementChecker $this
*/
return array(
array(
'name' => 'PHP version',
'mandatory' => true,
- 'condition' => version_compare(PHP_VERSION, '5.3.7', '>='),
+ 'condition' => version_compare(PHP_VERSION, '5.4.0', '>='),
'by' => 'Yii Framework ',
- 'memo' => 'PHP 5.3.7 or higher is required.',
+ 'memo' => 'PHP 5.4.0 or higher is required.',
),
array(
'name' => 'Reflection extension',
@@ -41,8 +41,10 @@ return array(
array(
'name' => 'Intl extension',
'mandatory' => false,
- 'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2'),
+ 'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2', '>='),
'by' => 'Internationalization support',
- 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use IDN -feature of EmailValidator or UrlValidator or the yii\i18n\Formatter
class.'
+ 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use advanced parameters formatting
+ in Yii::t()
, IDN -feature of
+ EmailValidator
or UrlValidator
or the yii\i18n\Formatter
class.'
),
);
diff --git a/framework/yii/requirements/views/console/index.php b/framework/yii/requirements/views/console/index.php
index 1b9c61b..1d87fe9 100644
--- a/framework/yii/requirements/views/console/index.php
+++ b/framework/yii/requirements/views/console/index.php
@@ -1,7 +1,7 @@
/*!
- * Bootstrap v2.3.2
+ * Bootstrap v3.0.0
*
- * Copyright 2012 Twitter, Inc
+ * Copyright 2013 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ * Designed and built with all the love in the world by @mdo and @fat.
*/
-.clearfix {
- *zoom: 1;
-}
-
-.clearfix:before,
-.clearfix:after {
- display: table;
- line-height: 0;
- content: "";
-}
-
-.clearfix:after {
- clear: both;
-}
-
-.hide-text {
- font: 0/0 a;
- color: transparent;
- text-shadow: none;
- background-color: transparent;
- border: 0;
-}
-
-.input-block-level {
- display: block;
- width: 100%;
- min-height: 30px;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
+/*! normalize.css v2.1.0 | MIT License | git.io/normalize */
article,
aside,
@@ -49,8 +19,10 @@ figure,
footer,
header,
hgroup,
+main,
nav,
-section {
+section,
+summary {
display: block;
}
@@ -58,31 +30,85 @@ audio,
canvas,
video {
display: inline-block;
- *display: inline;
- *zoom: 1;
}
audio:not([controls]) {
display: none;
+ height: 0;
+}
+
+[hidden] {
+ display: none;
}
html {
- font-size: 100%;
+ font-family: sans-serif;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
+body {
+ margin: 0;
+}
+
a:focus {
- outline: thin dotted #333;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
+ outline: thin dotted;
}
-a:hover,
-a:active {
+a:active,
+a:hover {
outline: 0;
}
+h1 {
+ margin: 0.67em 0;
+ font-size: 2em;
+}
+
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+
+b,
+strong {
+ font-weight: bold;
+}
+
+dfn {
+ font-style: italic;
+}
+
+hr {
+ height: 0;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+mark {
+ color: #000;
+ background: #ff0;
+}
+
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, serif;
+ font-size: 1em;
+}
+
+pre {
+ white-space: pre-wrap;
+}
+
+q {
+ quotes: "\201C" "\201D" "\2018" "\2019";
+}
+
+small {
+ font-size: 80%;
+}
+
sub,
sup {
position: relative;
@@ -100,17 +126,26 @@ sub {
}
img {
- width: auto\9;
- height: auto;
- max-width: 100%;
- vertical-align: middle;
border: 0;
- -ms-interpolation-mode: bicubic;
}
-#map_canvas img,
-.google-maps img {
- max-width: none;
+svg:not(:root) {
+ overflow: hidden;
+}
+
+figure {
+ margin: 0;
+}
+
+fieldset {
+ padding: 0.35em 0.625em 0.75em;
+ margin: 0 2px;
+ border: 1px solid #c0c0c0;
+}
+
+legend {
+ padding: 0;
+ border: 0;
}
button,
@@ -118,20 +153,18 @@ input,
select,
textarea {
margin: 0;
+ font-family: inherit;
font-size: 100%;
- vertical-align: middle;
}
button,
input {
- *overflow: visible;
line-height: normal;
}
-button::-moz-focus-inner,
-input::-moz-focus-inner {
- padding: 0;
- border: 0;
+button,
+select {
+ text-transform: none;
}
button,
@@ -142,15 +175,15 @@ input[type="submit"] {
-webkit-appearance: button;
}
-label,
-select,
-button,
-input[type="button"],
-input[type="reset"],
-input[type="submit"],
-input[type="radio"],
-input[type="checkbox"] {
- cursor: pointer;
+button[disabled],
+html input[disabled] {
+ cursor: default;
+}
+
+input[type="checkbox"],
+input[type="radio"] {
+ padding: 0;
+ box-sizing: border-box;
}
input[type="search"] {
@@ -160,16 +193,27 @@ input[type="search"] {
-webkit-appearance: textfield;
}
-input[type="search"]::-webkit-search-decoration,
-input[type="search"]::-webkit-search-cancel-button {
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
textarea {
overflow: auto;
vertical-align: top;
}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
@media print {
* {
color: #000 !important;
@@ -208,7 +252,7 @@ textarea {
max-width: 100% !important;
}
@page {
- margin: 0.5cm;
+ margin: 2cm .5cm;
}
p,
h2,
@@ -220,5063 +264,5451 @@ textarea {
h3 {
page-break-after: avoid;
}
+ .navbar {
+ display: none;
+ }
+ .table td,
+ .table th {
+ background-color: #fff !important;
+ }
+ .btn > .caret,
+ .dropup > .btn > .caret {
+ border-top-color: #000 !important;
+ }
+ .label {
+ border: 1px solid #000;
+ }
+ .table {
+ border-collapse: collapse !important;
+ }
+ .table-bordered th,
+ .table-bordered td {
+ border: 1px solid #ddd !important;
+ }
+}
+
+*,
+*:before,
+*:after {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+html {
+ font-size: 62.5%;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
body {
- margin: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
- line-height: 20px;
+ line-height: 1.428571429;
color: #333333;
background-color: #ffffff;
}
+input,
+button,
+select,
+textarea {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+
+button,
+input,
+select[multiple],
+textarea {
+ background-image: none;
+}
+
a {
- color: #0088cc;
+ color: #428bca;
text-decoration: none;
}
a:hover,
a:focus {
- color: #005580;
+ color: #2a6496;
text-decoration: underline;
}
-.img-rounded {
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
+a:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
}
-.img-polaroid {
- padding: 4px;
- background-color: #fff;
- border: 1px solid #ccc;
- border: 1px solid rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+img {
+ vertical-align: middle;
}
-.img-circle {
- -webkit-border-radius: 500px;
- -moz-border-radius: 500px;
- border-radius: 500px;
+.img-responsive {
+ display: block;
+ height: auto;
+ max-width: 100%;
}
-.row {
- margin-left: -20px;
- *zoom: 1;
+.img-rounded {
+ border-radius: 6px;
}
-.row:before,
-.row:after {
- display: table;
- line-height: 0;
- content: "";
+.img-thumbnail {
+ display: inline-block;
+ height: auto;
+ max-width: 100%;
+ padding: 4px;
+ line-height: 1.428571429;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 4px;
+ -webkit-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
}
-.row:after {
- clear: both;
+.img-circle {
+ border-radius: 50%;
}
-[class*="span"] {
- float: left;
- min-height: 1px;
- margin-left: 20px;
+hr {
+ margin-top: 20px;
+ margin-bottom: 20px;
+ border: 0;
+ border-top: 1px solid #eeeeee;
}
-.container,
-.navbar-static-top .container,
-.navbar-fixed-top .container,
-.navbar-fixed-bottom .container {
- width: 940px;
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0 0 0 0);
+ border: 0;
}
-.span12 {
- width: 940px;
+p {
+ margin: 0 0 10px;
}
-.span11 {
- width: 860px;
+.lead {
+ margin-bottom: 20px;
+ font-size: 16.099999999999998px;
+ font-weight: 200;
+ line-height: 1.4;
}
-.span10 {
- width: 780px;
+@media (min-width: 768px) {
+ .lead {
+ font-size: 21px;
+ }
}
-.span9 {
- width: 700px;
+small {
+ font-size: 85%;
}
-.span8 {
- width: 620px;
+cite {
+ font-style: normal;
}
-.span7 {
- width: 540px;
+.text-muted {
+ color: #999999;
}
-.span6 {
- width: 460px;
+.text-primary {
+ color: #428bca;
}
-.span5 {
- width: 380px;
+.text-warning {
+ color: #c09853;
}
-.span4 {
- width: 300px;
+.text-danger {
+ color: #b94a48;
}
-.span3 {
- width: 220px;
+.text-success {
+ color: #468847;
}
-.span2 {
- width: 140px;
+.text-info {
+ color: #3a87ad;
}
-.span1 {
- width: 60px;
+.text-left {
+ text-align: left;
}
-.offset12 {
- margin-left: 980px;
+.text-right {
+ text-align: right;
}
-.offset11 {
- margin-left: 900px;
+.text-center {
+ text-align: center;
}
-.offset10 {
- margin-left: 820px;
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6 {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 500;
+ line-height: 1.1;
}
-.offset9 {
- margin-left: 740px;
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small,
+.h1 small,
+.h2 small,
+.h3 small,
+.h4 small,
+.h5 small,
+.h6 small {
+ font-weight: normal;
+ line-height: 1;
+ color: #999999;
}
-.offset8 {
- margin-left: 660px;
+h1,
+h2,
+h3 {
+ margin-top: 20px;
+ margin-bottom: 10px;
}
-.offset7 {
- margin-left: 580px;
+h4,
+h5,
+h6 {
+ margin-top: 10px;
+ margin-bottom: 10px;
}
-.offset6 {
- margin-left: 500px;
+h1,
+.h1 {
+ font-size: 36px;
}
-.offset5 {
- margin-left: 420px;
+h2,
+.h2 {
+ font-size: 30px;
}
-.offset4 {
- margin-left: 340px;
+h3,
+.h3 {
+ font-size: 24px;
}
-.offset3 {
- margin-left: 260px;
+h4,
+.h4 {
+ font-size: 18px;
}
-.offset2 {
- margin-left: 180px;
+h5,
+.h5 {
+ font-size: 14px;
}
-.offset1 {
- margin-left: 100px;
+h6,
+.h6 {
+ font-size: 12px;
}
-.row-fluid {
- width: 100%;
- *zoom: 1;
+h1 small,
+.h1 small {
+ font-size: 24px;
}
-.row-fluid:before,
-.row-fluid:after {
- display: table;
- line-height: 0;
- content: "";
+h2 small,
+.h2 small {
+ font-size: 18px;
}
-.row-fluid:after {
- clear: both;
+h3 small,
+.h3 small,
+h4 small,
+.h4 small {
+ font-size: 14px;
}
-.row-fluid [class*="span"] {
- display: block;
- float: left;
- width: 100%;
- min-height: 30px;
- margin-left: 2.127659574468085%;
- *margin-left: 2.074468085106383%;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-
-.row-fluid [class*="span"]:first-child {
- margin-left: 0;
-}
-
-.row-fluid .controls-row [class*="span"] + [class*="span"] {
- margin-left: 2.127659574468085%;
-}
-
-.row-fluid .span12 {
- width: 100%;
- *width: 99.94680851063829%;
-}
-
-.row-fluid .span11 {
- width: 91.48936170212765%;
- *width: 91.43617021276594%;
-}
-
-.row-fluid .span10 {
- width: 82.97872340425532%;
- *width: 82.92553191489361%;
-}
-
-.row-fluid .span9 {
- width: 74.46808510638297%;
- *width: 74.41489361702126%;
-}
-
-.row-fluid .span8 {
- width: 65.95744680851064%;
- *width: 65.90425531914893%;
-}
-
-.row-fluid .span7 {
- width: 57.44680851063829%;
- *width: 57.39361702127659%;
-}
-
-.row-fluid .span6 {
- width: 48.93617021276595%;
- *width: 48.88297872340425%;
-}
-
-.row-fluid .span5 {
- width: 40.42553191489362%;
- *width: 40.37234042553192%;
-}
-
-.row-fluid .span4 {
- width: 31.914893617021278%;
- *width: 31.861702127659576%;
+.page-header {
+ padding-bottom: 9px;
+ margin: 40px 0 20px;
+ border-bottom: 1px solid #eeeeee;
}
-.row-fluid .span3 {
- width: 23.404255319148934%;
- *width: 23.351063829787233%;
+ul,
+ol {
+ margin-top: 0;
+ margin-bottom: 10px;
}
-.row-fluid .span2 {
- width: 14.893617021276595%;
- *width: 14.840425531914894%;
+ul ul,
+ol ul,
+ul ol,
+ol ol {
+ margin-bottom: 0;
}
-.row-fluid .span1 {
- width: 6.382978723404255%;
- *width: 6.329787234042553%;
+.list-unstyled {
+ padding-left: 0;
+ list-style: none;
}
-.row-fluid .offset12 {
- margin-left: 104.25531914893617%;
- *margin-left: 104.14893617021275%;
+.list-inline {
+ padding-left: 0;
+ list-style: none;
}
-.row-fluid .offset12:first-child {
- margin-left: 102.12765957446808%;
- *margin-left: 102.02127659574467%;
+.list-inline > li {
+ display: inline-block;
+ padding-right: 5px;
+ padding-left: 5px;
}
-.row-fluid .offset11 {
- margin-left: 95.74468085106382%;
- *margin-left: 95.6382978723404%;
+dl {
+ margin-bottom: 20px;
}
-.row-fluid .offset11:first-child {
- margin-left: 93.61702127659574%;
- *margin-left: 93.51063829787232%;
+dt,
+dd {
+ line-height: 1.428571429;
}
-.row-fluid .offset10 {
- margin-left: 87.23404255319149%;
- *margin-left: 87.12765957446807%;
+dt {
+ font-weight: bold;
}
-.row-fluid .offset10:first-child {
- margin-left: 85.1063829787234%;
- *margin-left: 84.99999999999999%;
+dd {
+ margin-left: 0;
}
-.row-fluid .offset9 {
- margin-left: 78.72340425531914%;
- *margin-left: 78.61702127659572%;
+@media (min-width: 768px) {
+ .dl-horizontal dt {
+ float: left;
+ width: 160px;
+ overflow: hidden;
+ clear: left;
+ text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .dl-horizontal dd {
+ margin-left: 180px;
+ }
+ .dl-horizontal dd:before,
+ .dl-horizontal dd:after {
+ display: table;
+ content: " ";
+ }
+ .dl-horizontal dd:after {
+ clear: both;
+ }
+ .dl-horizontal dd:before,
+ .dl-horizontal dd:after {
+ display: table;
+ content: " ";
+ }
+ .dl-horizontal dd:after {
+ clear: both;
+ }
}
-.row-fluid .offset9:first-child {
- margin-left: 76.59574468085106%;
- *margin-left: 76.48936170212764%;
+abbr[title],
+abbr[data-original-title] {
+ cursor: help;
+ border-bottom: 1px dotted #999999;
}
-.row-fluid .offset8 {
- margin-left: 70.2127659574468%;
- *margin-left: 70.10638297872339%;
+abbr.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
}
-.row-fluid .offset8:first-child {
- margin-left: 68.08510638297872%;
- *margin-left: 67.9787234042553%;
+blockquote {
+ padding: 10px 20px;
+ margin: 0 0 20px;
+ border-left: 5px solid #eeeeee;
}
-.row-fluid .offset7 {
- margin-left: 61.70212765957446%;
- *margin-left: 61.59574468085106%;
+blockquote p {
+ font-size: 17.5px;
+ font-weight: 300;
+ line-height: 1.25;
}
-.row-fluid .offset7:first-child {
- margin-left: 59.574468085106375%;
- *margin-left: 59.46808510638297%;
+blockquote p:last-child {
+ margin-bottom: 0;
}
-.row-fluid .offset6 {
- margin-left: 53.191489361702125%;
- *margin-left: 53.085106382978715%;
+blockquote small {
+ display: block;
+ line-height: 1.428571429;
+ color: #999999;
}
-.row-fluid .offset6:first-child {
- margin-left: 51.063829787234035%;
- *margin-left: 50.95744680851063%;
+blockquote small:before {
+ content: '\2014 \00A0';
}
-.row-fluid .offset5 {
- margin-left: 44.68085106382979%;
- *margin-left: 44.57446808510638%;
+blockquote.pull-right {
+ padding-right: 15px;
+ padding-left: 0;
+ border-right: 5px solid #eeeeee;
+ border-left: 0;
}
-.row-fluid .offset5:first-child {
- margin-left: 42.5531914893617%;
- *margin-left: 42.4468085106383%;
+blockquote.pull-right p,
+blockquote.pull-right small {
+ text-align: right;
}
-.row-fluid .offset4 {
- margin-left: 36.170212765957444%;
- *margin-left: 36.06382978723405%;
+blockquote.pull-right small:before {
+ content: '';
}
-.row-fluid .offset4:first-child {
- margin-left: 34.04255319148936%;
- *margin-left: 33.93617021276596%;
+blockquote.pull-right small:after {
+ content: '\00A0 \2014';
}
-.row-fluid .offset3 {
- margin-left: 27.659574468085104%;
- *margin-left: 27.5531914893617%;
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+ content: "";
}
-.row-fluid .offset3:first-child {
- margin-left: 25.53191489361702%;
- *margin-left: 25.425531914893618%;
+address {
+ display: block;
+ margin-bottom: 20px;
+ font-style: normal;
+ line-height: 1.428571429;
}
-.row-fluid .offset2 {
- margin-left: 19.148936170212764%;
- *margin-left: 19.04255319148936%;
+code,
+pre {
+ font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
}
-.row-fluid .offset2:first-child {
- margin-left: 17.02127659574468%;
- *margin-left: 16.914893617021278%;
+code {
+ padding: 2px 4px;
+ font-size: 90%;
+ color: #c7254e;
+ white-space: nowrap;
+ background-color: #f9f2f4;
+ border-radius: 4px;
}
-.row-fluid .offset1 {
- margin-left: 10.638297872340425%;
- *margin-left: 10.53191489361702%;
+pre {
+ display: block;
+ padding: 9.5px;
+ margin: 0 0 10px;
+ font-size: 13px;
+ line-height: 1.428571429;
+ color: #333333;
+ word-break: break-all;
+ word-wrap: break-word;
+ background-color: #f5f5f5;
+ border: 1px solid #cccccc;
+ border-radius: 4px;
}
-.row-fluid .offset1:first-child {
- margin-left: 8.51063829787234%;
- *margin-left: 8.404255319148938%;
+pre.prettyprint {
+ margin-bottom: 20px;
}
-[class*="span"].hide,
-.row-fluid [class*="span"].hide {
- display: none;
+pre code {
+ padding: 0;
+ font-size: inherit;
+ color: inherit;
+ white-space: pre-wrap;
+ background-color: transparent;
+ border: 0;
}
-[class*="span"].pull-right,
-.row-fluid [class*="span"].pull-right {
- float: right;
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
}
.container {
+ padding-right: 15px;
+ padding-left: 15px;
margin-right: auto;
margin-left: auto;
- *zoom: 1;
}
.container:before,
.container:after {
display: table;
- line-height: 0;
- content: "";
+ content: " ";
}
.container:after {
clear: both;
}
-.container-fluid {
- padding-right: 20px;
- padding-left: 20px;
- *zoom: 1;
-}
-
-.container-fluid:before,
-.container-fluid:after {
+.container:before,
+.container:after {
display: table;
- line-height: 0;
- content: "";
+ content: " ";
}
-.container-fluid:after {
+.container:after {
clear: both;
}
-p {
- margin: 0 0 10px;
+.row {
+ margin-right: -15px;
+ margin-left: -15px;
}
-.lead {
- margin-bottom: 20px;
- font-size: 21px;
- font-weight: 200;
- line-height: 30px;
+.row:before,
+.row:after {
+ display: table;
+ content: " ";
}
-small {
- font-size: 85%;
+.row:after {
+ clear: both;
}
-strong {
- font-weight: bold;
+.row:before,
+.row:after {
+ display: table;
+ content: " ";
}
-em {
- font-style: italic;
+.row:after {
+ clear: both;
}
-cite {
- font-style: normal;
+.col-xs-1,
+.col-xs-2,
+.col-xs-3,
+.col-xs-4,
+.col-xs-5,
+.col-xs-6,
+.col-xs-7,
+.col-xs-8,
+.col-xs-9,
+.col-xs-10,
+.col-xs-11,
+.col-xs-12,
+.col-sm-1,
+.col-sm-2,
+.col-sm-3,
+.col-sm-4,
+.col-sm-5,
+.col-sm-6,
+.col-sm-7,
+.col-sm-8,
+.col-sm-9,
+.col-sm-10,
+.col-sm-11,
+.col-sm-12,
+.col-md-1,
+.col-md-2,
+.col-md-3,
+.col-md-4,
+.col-md-5,
+.col-md-6,
+.col-md-7,
+.col-md-8,
+.col-md-9,
+.col-md-10,
+.col-md-11,
+.col-md-12,
+.col-lg-1,
+.col-lg-2,
+.col-lg-3,
+.col-lg-4,
+.col-lg-5,
+.col-lg-6,
+.col-lg-7,
+.col-lg-8,
+.col-lg-9,
+.col-lg-10,
+.col-lg-11,
+.col-lg-12 {
+ position: relative;
+ min-height: 1px;
+ padding-right: 15px;
+ padding-left: 15px;
}
-.muted {
- color: #999999;
+.col-xs-1,
+.col-xs-2,
+.col-xs-3,
+.col-xs-4,
+.col-xs-5,
+.col-xs-6,
+.col-xs-7,
+.col-xs-8,
+.col-xs-9,
+.col-xs-10,
+.col-xs-11 {
+ float: left;
}
-a.muted:hover,
-a.muted:focus {
- color: #808080;
+.col-xs-1 {
+ width: 8.333333333333332%;
}
-.text-warning {
- color: #c09853;
+.col-xs-2 {
+ width: 16.666666666666664%;
}
-a.text-warning:hover,
-a.text-warning:focus {
- color: #a47e3c;
+.col-xs-3 {
+ width: 25%;
}
-.text-error {
- color: #b94a48;
+.col-xs-4 {
+ width: 33.33333333333333%;
}
-a.text-error:hover,
-a.text-error:focus {
- color: #953b39;
+.col-xs-5 {
+ width: 41.66666666666667%;
}
-.text-info {
- color: #3a87ad;
+.col-xs-6 {
+ width: 50%;
}
-a.text-info:hover,
-a.text-info:focus {
- color: #2d6987;
+.col-xs-7 {
+ width: 58.333333333333336%;
}
-.text-success {
- color: #468847;
+.col-xs-8 {
+ width: 66.66666666666666%;
}
-a.text-success:hover,
-a.text-success:focus {
- color: #356635;
+.col-xs-9 {
+ width: 75%;
}
-.text-left {
- text-align: left;
+.col-xs-10 {
+ width: 83.33333333333334%;
}
-.text-right {
- text-align: right;
+.col-xs-11 {
+ width: 91.66666666666666%;
}
-.text-center {
- text-align: center;
+.col-xs-12 {
+ width: 100%;
}
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- margin: 10px 0;
- font-family: inherit;
- font-weight: bold;
- line-height: 20px;
- color: inherit;
- text-rendering: optimizelegibility;
+@media (min-width: 768px) {
+ .container {
+ max-width: 750px;
+ }
+ .col-sm-1,
+ .col-sm-2,
+ .col-sm-3,
+ .col-sm-4,
+ .col-sm-5,
+ .col-sm-6,
+ .col-sm-7,
+ .col-sm-8,
+ .col-sm-9,
+ .col-sm-10,
+ .col-sm-11 {
+ float: left;
+ }
+ .col-sm-1 {
+ width: 8.333333333333332%;
+ }
+ .col-sm-2 {
+ width: 16.666666666666664%;
+ }
+ .col-sm-3 {
+ width: 25%;
+ }
+ .col-sm-4 {
+ width: 33.33333333333333%;
+ }
+ .col-sm-5 {
+ width: 41.66666666666667%;
+ }
+ .col-sm-6 {
+ width: 50%;
+ }
+ .col-sm-7 {
+ width: 58.333333333333336%;
+ }
+ .col-sm-8 {
+ width: 66.66666666666666%;
+ }
+ .col-sm-9 {
+ width: 75%;
+ }
+ .col-sm-10 {
+ width: 83.33333333333334%;
+ }
+ .col-sm-11 {
+ width: 91.66666666666666%;
+ }
+ .col-sm-12 {
+ width: 100%;
+ }
+ .col-sm-push-1 {
+ left: 8.333333333333332%;
+ }
+ .col-sm-push-2 {
+ left: 16.666666666666664%;
+ }
+ .col-sm-push-3 {
+ left: 25%;
+ }
+ .col-sm-push-4 {
+ left: 33.33333333333333%;
+ }
+ .col-sm-push-5 {
+ left: 41.66666666666667%;
+ }
+ .col-sm-push-6 {
+ left: 50%;
+ }
+ .col-sm-push-7 {
+ left: 58.333333333333336%;
+ }
+ .col-sm-push-8 {
+ left: 66.66666666666666%;
+ }
+ .col-sm-push-9 {
+ left: 75%;
+ }
+ .col-sm-push-10 {
+ left: 83.33333333333334%;
+ }
+ .col-sm-push-11 {
+ left: 91.66666666666666%;
+ }
+ .col-sm-pull-1 {
+ right: 8.333333333333332%;
+ }
+ .col-sm-pull-2 {
+ right: 16.666666666666664%;
+ }
+ .col-sm-pull-3 {
+ right: 25%;
+ }
+ .col-sm-pull-4 {
+ right: 33.33333333333333%;
+ }
+ .col-sm-pull-5 {
+ right: 41.66666666666667%;
+ }
+ .col-sm-pull-6 {
+ right: 50%;
+ }
+ .col-sm-pull-7 {
+ right: 58.333333333333336%;
+ }
+ .col-sm-pull-8 {
+ right: 66.66666666666666%;
+ }
+ .col-sm-pull-9 {
+ right: 75%;
+ }
+ .col-sm-pull-10 {
+ right: 83.33333333333334%;
+ }
+ .col-sm-pull-11 {
+ right: 91.66666666666666%;
+ }
+ .col-sm-offset-1 {
+ margin-left: 8.333333333333332%;
+ }
+ .col-sm-offset-2 {
+ margin-left: 16.666666666666664%;
+ }
+ .col-sm-offset-3 {
+ margin-left: 25%;
+ }
+ .col-sm-offset-4 {
+ margin-left: 33.33333333333333%;
+ }
+ .col-sm-offset-5 {
+ margin-left: 41.66666666666667%;
+ }
+ .col-sm-offset-6 {
+ margin-left: 50%;
+ }
+ .col-sm-offset-7 {
+ margin-left: 58.333333333333336%;
+ }
+ .col-sm-offset-8 {
+ margin-left: 66.66666666666666%;
+ }
+ .col-sm-offset-9 {
+ margin-left: 75%;
+ }
+ .col-sm-offset-10 {
+ margin-left: 83.33333333333334%;
+ }
+ .col-sm-offset-11 {
+ margin-left: 91.66666666666666%;
+ }
}
-h1 small,
-h2 small,
-h3 small,
-h4 small,
-h5 small,
-h6 small {
- font-weight: normal;
- line-height: 1;
- color: #999999;
+@media (min-width: 992px) {
+ .container {
+ max-width: 970px;
+ }
+ .col-md-1,
+ .col-md-2,
+ .col-md-3,
+ .col-md-4,
+ .col-md-5,
+ .col-md-6,
+ .col-md-7,
+ .col-md-8,
+ .col-md-9,
+ .col-md-10,
+ .col-md-11 {
+ float: left;
+ }
+ .col-md-1 {
+ width: 8.333333333333332%;
+ }
+ .col-md-2 {
+ width: 16.666666666666664%;
+ }
+ .col-md-3 {
+ width: 25%;
+ }
+ .col-md-4 {
+ width: 33.33333333333333%;
+ }
+ .col-md-5 {
+ width: 41.66666666666667%;
+ }
+ .col-md-6 {
+ width: 50%;
+ }
+ .col-md-7 {
+ width: 58.333333333333336%;
+ }
+ .col-md-8 {
+ width: 66.66666666666666%;
+ }
+ .col-md-9 {
+ width: 75%;
+ }
+ .col-md-10 {
+ width: 83.33333333333334%;
+ }
+ .col-md-11 {
+ width: 91.66666666666666%;
+ }
+ .col-md-12 {
+ width: 100%;
+ }
+ .col-md-push-0 {
+ left: auto;
+ }
+ .col-md-push-1 {
+ left: 8.333333333333332%;
+ }
+ .col-md-push-2 {
+ left: 16.666666666666664%;
+ }
+ .col-md-push-3 {
+ left: 25%;
+ }
+ .col-md-push-4 {
+ left: 33.33333333333333%;
+ }
+ .col-md-push-5 {
+ left: 41.66666666666667%;
+ }
+ .col-md-push-6 {
+ left: 50%;
+ }
+ .col-md-push-7 {
+ left: 58.333333333333336%;
+ }
+ .col-md-push-8 {
+ left: 66.66666666666666%;
+ }
+ .col-md-push-9 {
+ left: 75%;
+ }
+ .col-md-push-10 {
+ left: 83.33333333333334%;
+ }
+ .col-md-push-11 {
+ left: 91.66666666666666%;
+ }
+ .col-md-pull-0 {
+ right: auto;
+ }
+ .col-md-pull-1 {
+ right: 8.333333333333332%;
+ }
+ .col-md-pull-2 {
+ right: 16.666666666666664%;
+ }
+ .col-md-pull-3 {
+ right: 25%;
+ }
+ .col-md-pull-4 {
+ right: 33.33333333333333%;
+ }
+ .col-md-pull-5 {
+ right: 41.66666666666667%;
+ }
+ .col-md-pull-6 {
+ right: 50%;
+ }
+ .col-md-pull-7 {
+ right: 58.333333333333336%;
+ }
+ .col-md-pull-8 {
+ right: 66.66666666666666%;
+ }
+ .col-md-pull-9 {
+ right: 75%;
+ }
+ .col-md-pull-10 {
+ right: 83.33333333333334%;
+ }
+ .col-md-pull-11 {
+ right: 91.66666666666666%;
+ }
+ .col-md-offset-0 {
+ margin-left: 0;
+ }
+ .col-md-offset-1 {
+ margin-left: 8.333333333333332%;
+ }
+ .col-md-offset-2 {
+ margin-left: 16.666666666666664%;
+ }
+ .col-md-offset-3 {
+ margin-left: 25%;
+ }
+ .col-md-offset-4 {
+ margin-left: 33.33333333333333%;
+ }
+ .col-md-offset-5 {
+ margin-left: 41.66666666666667%;
+ }
+ .col-md-offset-6 {
+ margin-left: 50%;
+ }
+ .col-md-offset-7 {
+ margin-left: 58.333333333333336%;
+ }
+ .col-md-offset-8 {
+ margin-left: 66.66666666666666%;
+ }
+ .col-md-offset-9 {
+ margin-left: 75%;
+ }
+ .col-md-offset-10 {
+ margin-left: 83.33333333333334%;
+ }
+ .col-md-offset-11 {
+ margin-left: 91.66666666666666%;
+ }
}
-h1,
-h2,
-h3 {
- line-height: 40px;
+@media (min-width: 1200px) {
+ .container {
+ max-width: 1170px;
+ }
+ .col-lg-1,
+ .col-lg-2,
+ .col-lg-3,
+ .col-lg-4,
+ .col-lg-5,
+ .col-lg-6,
+ .col-lg-7,
+ .col-lg-8,
+ .col-lg-9,
+ .col-lg-10,
+ .col-lg-11 {
+ float: left;
+ }
+ .col-lg-1 {
+ width: 8.333333333333332%;
+ }
+ .col-lg-2 {
+ width: 16.666666666666664%;
+ }
+ .col-lg-3 {
+ width: 25%;
+ }
+ .col-lg-4 {
+ width: 33.33333333333333%;
+ }
+ .col-lg-5 {
+ width: 41.66666666666667%;
+ }
+ .col-lg-6 {
+ width: 50%;
+ }
+ .col-lg-7 {
+ width: 58.333333333333336%;
+ }
+ .col-lg-8 {
+ width: 66.66666666666666%;
+ }
+ .col-lg-9 {
+ width: 75%;
+ }
+ .col-lg-10 {
+ width: 83.33333333333334%;
+ }
+ .col-lg-11 {
+ width: 91.66666666666666%;
+ }
+ .col-lg-12 {
+ width: 100%;
+ }
+ .col-lg-push-0 {
+ left: auto;
+ }
+ .col-lg-push-1 {
+ left: 8.333333333333332%;
+ }
+ .col-lg-push-2 {
+ left: 16.666666666666664%;
+ }
+ .col-lg-push-3 {
+ left: 25%;
+ }
+ .col-lg-push-4 {
+ left: 33.33333333333333%;
+ }
+ .col-lg-push-5 {
+ left: 41.66666666666667%;
+ }
+ .col-lg-push-6 {
+ left: 50%;
+ }
+ .col-lg-push-7 {
+ left: 58.333333333333336%;
+ }
+ .col-lg-push-8 {
+ left: 66.66666666666666%;
+ }
+ .col-lg-push-9 {
+ left: 75%;
+ }
+ .col-lg-push-10 {
+ left: 83.33333333333334%;
+ }
+ .col-lg-push-11 {
+ left: 91.66666666666666%;
+ }
+ .col-lg-pull-0 {
+ right: auto;
+ }
+ .col-lg-pull-1 {
+ right: 8.333333333333332%;
+ }
+ .col-lg-pull-2 {
+ right: 16.666666666666664%;
+ }
+ .col-lg-pull-3 {
+ right: 25%;
+ }
+ .col-lg-pull-4 {
+ right: 33.33333333333333%;
+ }
+ .col-lg-pull-5 {
+ right: 41.66666666666667%;
+ }
+ .col-lg-pull-6 {
+ right: 50%;
+ }
+ .col-lg-pull-7 {
+ right: 58.333333333333336%;
+ }
+ .col-lg-pull-8 {
+ right: 66.66666666666666%;
+ }
+ .col-lg-pull-9 {
+ right: 75%;
+ }
+ .col-lg-pull-10 {
+ right: 83.33333333333334%;
+ }
+ .col-lg-pull-11 {
+ right: 91.66666666666666%;
+ }
+ .col-lg-offset-0 {
+ margin-left: 0;
+ }
+ .col-lg-offset-1 {
+ margin-left: 8.333333333333332%;
+ }
+ .col-lg-offset-2 {
+ margin-left: 16.666666666666664%;
+ }
+ .col-lg-offset-3 {
+ margin-left: 25%;
+ }
+ .col-lg-offset-4 {
+ margin-left: 33.33333333333333%;
+ }
+ .col-lg-offset-5 {
+ margin-left: 41.66666666666667%;
+ }
+ .col-lg-offset-6 {
+ margin-left: 50%;
+ }
+ .col-lg-offset-7 {
+ margin-left: 58.333333333333336%;
+ }
+ .col-lg-offset-8 {
+ margin-left: 66.66666666666666%;
+ }
+ .col-lg-offset-9 {
+ margin-left: 75%;
+ }
+ .col-lg-offset-10 {
+ margin-left: 83.33333333333334%;
+ }
+ .col-lg-offset-11 {
+ margin-left: 91.66666666666666%;
+ }
}
-h1 {
- font-size: 38.5px;
+table {
+ max-width: 100%;
+ background-color: transparent;
}
-h2 {
- font-size: 31.5px;
+th {
+ text-align: left;
}
-h3 {
- font-size: 24.5px;
+.table {
+ width: 100%;
+ margin-bottom: 20px;
}
-h4 {
- font-size: 17.5px;
+.table thead > tr > th,
+.table tbody > tr > th,
+.table tfoot > tr > th,
+.table thead > tr > td,
+.table tbody > tr > td,
+.table tfoot > tr > td {
+ padding: 8px;
+ line-height: 1.428571429;
+ vertical-align: top;
+ border-top: 1px solid #dddddd;
}
-h5 {
- font-size: 14px;
+.table thead > tr > th {
+ vertical-align: bottom;
+ border-bottom: 2px solid #dddddd;
}
-h6 {
- font-size: 11.9px;
+.table caption + thead tr:first-child th,
+.table colgroup + thead tr:first-child th,
+.table thead:first-child tr:first-child th,
+.table caption + thead tr:first-child td,
+.table colgroup + thead tr:first-child td,
+.table thead:first-child tr:first-child td {
+ border-top: 0;
}
-h1 small {
- font-size: 24.5px;
+.table tbody + tbody {
+ border-top: 2px solid #dddddd;
}
-h2 small {
- font-size: 17.5px;
+.table .table {
+ background-color: #ffffff;
}
-h3 small {
- font-size: 14px;
+.table-condensed thead > tr > th,
+.table-condensed tbody > tr > th,
+.table-condensed tfoot > tr > th,
+.table-condensed thead > tr > td,
+.table-condensed tbody > tr > td,
+.table-condensed tfoot > tr > td {
+ padding: 5px;
}
-h4 small {
- font-size: 14px;
+.table-bordered {
+ border: 1px solid #dddddd;
}
-.page-header {
- padding-bottom: 9px;
- margin: 20px 0 30px;
- border-bottom: 1px solid #eeeeee;
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+ border: 1px solid #dddddd;
}
-ul,
-ol {
- padding: 0;
- margin: 0 0 10px 25px;
+.table-bordered > thead > tr > th,
+.table-bordered > thead > tr > td {
+ border-bottom-width: 2px;
}
-ul ul,
-ul ol,
-ol ol,
-ol ul {
- margin-bottom: 0;
+.table-striped > tbody > tr:nth-child(odd) > td,
+.table-striped > tbody > tr:nth-child(odd) > th {
+ background-color: #f9f9f9;
}
-li {
- line-height: 20px;
+.table-hover > tbody > tr:hover > td,
+.table-hover > tbody > tr:hover > th {
+ background-color: #f5f5f5;
}
-ul.unstyled,
-ol.unstyled {
- margin-left: 0;
- list-style: none;
+table col[class*="col-"] {
+ display: table-column;
+ float: none;
}
-ul.inline,
-ol.inline {
- margin-left: 0;
- list-style: none;
+table td[class*="col-"],
+table th[class*="col-"] {
+ display: table-cell;
+ float: none;
}
-ul.inline > li,
-ol.inline > li {
- display: inline-block;
- *display: inline;
- padding-right: 5px;
- padding-left: 5px;
- *zoom: 1;
+.table > thead > tr > td.active,
+.table > tbody > tr > td.active,
+.table > tfoot > tr > td.active,
+.table > thead > tr > th.active,
+.table > tbody > tr > th.active,
+.table > tfoot > tr > th.active,
+.table > thead > tr.active > td,
+.table > tbody > tr.active > td,
+.table > tfoot > tr.active > td,
+.table > thead > tr.active > th,
+.table > tbody > tr.active > th,
+.table > tfoot > tr.active > th {
+ background-color: #f5f5f5;
}
-dl {
- margin-bottom: 20px;
+.table > thead > tr > td.success,
+.table > tbody > tr > td.success,
+.table > tfoot > tr > td.success,
+.table > thead > tr > th.success,
+.table > tbody > tr > th.success,
+.table > tfoot > tr > th.success,
+.table > thead > tr.success > td,
+.table > tbody > tr.success > td,
+.table > tfoot > tr.success > td,
+.table > thead > tr.success > th,
+.table > tbody > tr.success > th,
+.table > tfoot > tr.success > th {
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
}
-dt,
-dd {
- line-height: 20px;
+.table-hover > tbody > tr > td.success:hover,
+.table-hover > tbody > tr > th.success:hover,
+.table-hover > tbody > tr.success:hover > td {
+ background-color: #d0e9c6;
+ border-color: #c9e2b3;
+}
+
+.table > thead > tr > td.danger,
+.table > tbody > tr > td.danger,
+.table > tfoot > tr > td.danger,
+.table > thead > tr > th.danger,
+.table > tbody > tr > th.danger,
+.table > tfoot > tr > th.danger,
+.table > thead > tr.danger > td,
+.table > tbody > tr.danger > td,
+.table > tfoot > tr.danger > td,
+.table > thead > tr.danger > th,
+.table > tbody > tr.danger > th,
+.table > tfoot > tr.danger > th {
+ background-color: #f2dede;
+ border-color: #eed3d7;
}
-dt {
+.table-hover > tbody > tr > td.danger:hover,
+.table-hover > tbody > tr > th.danger:hover,
+.table-hover > tbody > tr.danger:hover > td {
+ background-color: #ebcccc;
+ border-color: #e6c1c7;
+}
+
+.table > thead > tr > td.warning,
+.table > tbody > tr > td.warning,
+.table > tfoot > tr > td.warning,
+.table > thead > tr > th.warning,
+.table > tbody > tr > th.warning,
+.table > tfoot > tr > th.warning,
+.table > thead > tr.warning > td,
+.table > tbody > tr.warning > td,
+.table > tfoot > tr.warning > td,
+.table > thead > tr.warning > th,
+.table > tbody > tr.warning > th,
+.table > tfoot > tr.warning > th {
+ background-color: #fcf8e3;
+ border-color: #fbeed5;
+}
+
+.table-hover > tbody > tr > td.warning:hover,
+.table-hover > tbody > tr > th.warning:hover,
+.table-hover > tbody > tr.warning:hover > td {
+ background-color: #faf2cc;
+ border-color: #f8e5be;
+}
+
+@media (max-width: 768px) {
+ .table-responsive {
+ width: 100%;
+ margin-bottom: 15px;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ border: 1px solid #dddddd;
+ }
+ .table-responsive > .table {
+ margin-bottom: 0;
+ background-color: #fff;
+ }
+ .table-responsive > .table > thead > tr > th,
+ .table-responsive > .table > tbody > tr > th,
+ .table-responsive > .table > tfoot > tr > th,
+ .table-responsive > .table > thead > tr > td,
+ .table-responsive > .table > tbody > tr > td,
+ .table-responsive > .table > tfoot > tr > td {
+ white-space: nowrap;
+ }
+ .table-responsive > .table-bordered {
+ border: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr > th:first-child,
+ .table-responsive > .table-bordered > tbody > tr > th:first-child,
+ .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+ .table-responsive > .table-bordered > thead > tr > td:first-child,
+ .table-responsive > .table-bordered > tbody > tr > td:first-child,
+ .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+ border-left: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr > th:last-child,
+ .table-responsive > .table-bordered > tbody > tr > th:last-child,
+ .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+ .table-responsive > .table-bordered > thead > tr > td:last-child,
+ .table-responsive > .table-bordered > tbody > tr > td:last-child,
+ .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+ border-right: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr:last-child > th,
+ .table-responsive > .table-bordered > tbody > tr:last-child > th,
+ .table-responsive > .table-bordered > tfoot > tr:last-child > th,
+ .table-responsive > .table-bordered > thead > tr:last-child > td,
+ .table-responsive > .table-bordered > tbody > tr:last-child > td,
+ .table-responsive > .table-bordered > tfoot > tr:last-child > td {
+ border-bottom: 0;
+ }
+}
+
+fieldset {
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: inherit;
+ color: #333333;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5;
+}
+
+label {
+ display: inline-block;
+ margin-bottom: 5px;
font-weight: bold;
}
-dd {
+input[type="search"] {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 4px 0 0;
+ margin-top: 1px \9;
+ /* IE8-9 */
+
+ line-height: normal;
+}
+
+input[type="file"] {
+ display: block;
+}
+
+select[multiple],
+select[size] {
+ height: auto;
+}
+
+select optgroup {
+ font-family: inherit;
+ font-size: inherit;
+ font-style: inherit;
+}
+
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+input[type="number"]::-webkit-outer-spin-button,
+input[type="number"]::-webkit-inner-spin-button {
+ height: auto;
+}
+
+.form-control:-moz-placeholder {
+ color: #999999;
+}
+
+.form-control::-moz-placeholder {
+ color: #999999;
+}
+
+.form-control:-ms-input-placeholder {
+ color: #999999;
+}
+
+.form-control::-webkit-input-placeholder {
+ color: #999999;
+}
+
+.form-control {
+ display: block;
+ width: 100%;
+ height: 34px;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.428571429;
+ color: #555555;
+ vertical-align: middle;
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
+ transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
+}
+
+.form-control:focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
+}
+
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+ cursor: not-allowed;
+ background-color: #eeeeee;
+}
+
+textarea.form-control {
+ height: auto;
+}
+
+.form-group {
+ margin-bottom: 15px;
+}
+
+.radio,
+.checkbox {
+ display: block;
+ min-height: 20px;
+ padding-left: 20px;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ vertical-align: middle;
+}
+
+.radio label,
+.checkbox label {
+ display: inline;
+ margin-bottom: 0;
+ font-weight: normal;
+ cursor: pointer;
+}
+
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+ float: left;
+ margin-left: -20px;
+}
+
+.radio + .radio,
+.checkbox + .checkbox {
+ margin-top: -5px;
+}
+
+.radio-inline,
+.checkbox-inline {
+ display: inline-block;
+ padding-left: 20px;
+ margin-bottom: 0;
+ font-weight: normal;
+ vertical-align: middle;
+ cursor: pointer;
+}
+
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+ margin-top: 0;
margin-left: 10px;
}
-.dl-horizontal {
- *zoom: 1;
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+.radio[disabled],
+.radio-inline[disabled],
+.checkbox[disabled],
+.checkbox-inline[disabled],
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"],
+fieldset[disabled] .radio,
+fieldset[disabled] .radio-inline,
+fieldset[disabled] .checkbox,
+fieldset[disabled] .checkbox-inline {
+ cursor: not-allowed;
}
-.dl-horizontal:before,
-.dl-horizontal:after {
- display: table;
- line-height: 0;
- content: "";
+.input-sm {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+
+select.input-sm {
+ height: 30px;
+ line-height: 30px;
+}
+
+textarea.input-sm {
+ height: auto;
+}
+
+.input-lg {
+ height: 45px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+
+select.input-lg {
+ height: 45px;
+ line-height: 45px;
+}
+
+textarea.input-lg {
+ height: auto;
+}
+
+.has-warning .help-block,
+.has-warning .control-label {
+ color: #c09853;
+}
+
+.has-warning .form-control {
+ border-color: #c09853;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.has-warning .form-control:focus {
+ border-color: #a47e3c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+}
+
+.has-warning .input-group-addon {
+ color: #c09853;
+ background-color: #fcf8e3;
+ border-color: #c09853;
+}
+
+.has-error .help-block,
+.has-error .control-label {
+ color: #b94a48;
+}
+
+.has-error .form-control {
+ border-color: #b94a48;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.has-error .form-control:focus {
+ border-color: #953b39;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+}
+
+.has-error .input-group-addon {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #b94a48;
+}
+
+.has-success .help-block,
+.has-success .control-label {
+ color: #468847;
+}
+
+.has-success .form-control {
+ border-color: #468847;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.has-success .form-control:focus {
+ border-color: #356635;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+}
+
+.has-success .input-group-addon {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #468847;
+}
+
+.form-control-static {
+ padding-top: 7px;
+ margin-bottom: 0;
+}
+
+.help-block {
+ display: block;
+ margin-top: 5px;
+ margin-bottom: 10px;
+ color: #737373;
+}
+
+@media (min-width: 768px) {
+ .form-inline .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .form-inline .form-control {
+ display: inline-block;
+ }
+ .form-inline .radio,
+ .form-inline .checkbox {
+ display: inline-block;
+ padding-left: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+ .form-inline .radio input[type="radio"],
+ .form-inline .checkbox input[type="checkbox"] {
+ float: none;
+ margin-left: 0;
+ }
}
-.dl-horizontal:after {
- clear: both;
+.form-horizontal .control-label,
+.form-horizontal .radio,
+.form-horizontal .checkbox,
+.form-horizontal .radio-inline,
+.form-horizontal .checkbox-inline {
+ padding-top: 7px;
+ margin-top: 0;
+ margin-bottom: 0;
}
-.dl-horizontal dt {
- float: left;
- width: 160px;
- overflow: hidden;
- clear: left;
- text-align: right;
- text-overflow: ellipsis;
- white-space: nowrap;
+.form-horizontal .form-group {
+ margin-right: -15px;
+ margin-left: -15px;
}
-.dl-horizontal dd {
- margin-left: 180px;
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after {
+ display: table;
+ content: " ";
}
-hr {
- margin: 20px 0;
- border: 0;
- border-top: 1px solid #eeeeee;
- border-bottom: 1px solid #ffffff;
+.form-horizontal .form-group:after {
+ clear: both;
}
-abbr[title],
-abbr[data-original-title] {
- cursor: help;
- border-bottom: 1px dotted #999999;
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after {
+ display: table;
+ content: " ";
}
-abbr.initialism {
- font-size: 90%;
- text-transform: uppercase;
+.form-horizontal .form-group:after {
+ clear: both;
}
-blockquote {
- padding: 0 0 0 15px;
- margin: 0 0 20px;
- border-left: 5px solid #eeeeee;
+@media (min-width: 768px) {
+ .form-horizontal .control-label {
+ text-align: right;
+ }
}
-blockquote p {
+.btn {
+ display: inline-block;
+ padding: 6px 12px;
margin-bottom: 0;
- font-size: 17.5px;
- font-weight: 300;
- line-height: 1.25;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 1.428571429;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ cursor: pointer;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
}
-blockquote small {
- display: block;
- line-height: 20px;
- color: #999999;
+.btn:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
}
-blockquote small:before {
- content: '\2014 \00A0';
+.btn:hover,
+.btn:focus {
+ color: #333333;
+ text-decoration: none;
}
-blockquote.pull-right {
- float: right;
- padding-right: 15px;
- padding-left: 0;
- border-right: 5px solid #eeeeee;
- border-left: 0;
+.btn:active,
+.btn.active {
+ background-image: none;
+ outline: 0;
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
-blockquote.pull-right p,
-blockquote.pull-right small {
- text-align: right;
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn {
+ pointer-events: none;
+ cursor: not-allowed;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ box-shadow: none;
}
-blockquote.pull-right small:before {
- content: '';
+.btn-default {
+ color: #333333;
+ background-color: #ffffff;
+ border-color: #cccccc;
}
-blockquote.pull-right small:after {
- content: '\00A0 \2014';
+.btn-default:hover,
+.btn-default:focus,
+.btn-default:active,
+.btn-default.active,
+.open .dropdown-toggle.btn-default {
+ color: #333333;
+ background-color: #ebebeb;
+ border-color: #adadad;
}
-q:before,
-q:after,
-blockquote:before,
-blockquote:after {
- content: "";
+.btn-default:active,
+.btn-default.active,
+.open .dropdown-toggle.btn-default {
+ background-image: none;
}
-address {
- display: block;
- margin-bottom: 20px;
- font-style: normal;
- line-height: 20px;
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+ background-color: #ffffff;
+ border-color: #cccccc;
}
-code,
-pre {
- padding: 0 3px 2px;
- font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
- font-size: 12px;
- color: #333333;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
+.btn-primary {
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #357ebd;
}
-code {
- padding: 2px 4px;
- color: #d14;
- white-space: nowrap;
- background-color: #f7f7f9;
- border: 1px solid #e1e1e8;
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary:active,
+.btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+ color: #ffffff;
+ background-color: #3276b1;
+ border-color: #285e8e;
}
-pre {
- display: block;
- padding: 9.5px;
- margin: 0 0 10px;
- font-size: 13px;
- line-height: 20px;
- word-break: break-all;
- word-wrap: break-word;
- white-space: pre;
- white-space: pre-wrap;
- background-color: #f5f5f5;
- border: 1px solid #ccc;
- border: 1px solid rgba(0, 0, 0, 0.15);
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
+.btn-primary:active,
+.btn-primary.active,
+.open .dropdown-toggle.btn-primary {
+ background-image: none;
}
-pre.prettyprint {
- margin-bottom: 20px;
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+ background-color: #428bca;
+ border-color: #357ebd;
}
-pre code {
- padding: 0;
- color: inherit;
- white-space: pre;
- white-space: pre-wrap;
- background-color: transparent;
- border: 0;
+.btn-warning {
+ color: #ffffff;
+ background-color: #f0ad4e;
+ border-color: #eea236;
}
-.pre-scrollable {
- max-height: 340px;
- overflow-y: scroll;
+.btn-warning:hover,
+.btn-warning:focus,
+.btn-warning:active,
+.btn-warning.active,
+.open .dropdown-toggle.btn-warning {
+ color: #ffffff;
+ background-color: #ed9c28;
+ border-color: #d58512;
}
-form {
- margin: 0 0 20px;
+.btn-warning:active,
+.btn-warning.active,
+.open .dropdown-toggle.btn-warning {
+ background-image: none;
}
-fieldset {
- padding: 0;
- margin: 0;
- border: 0;
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+ background-color: #f0ad4e;
+ border-color: #eea236;
}
-legend {
- display: block;
- width: 100%;
- padding: 0;
- margin-bottom: 20px;
- font-size: 21px;
- line-height: 40px;
- color: #333333;
- border: 0;
- border-bottom: 1px solid #e5e5e5;
+.btn-danger {
+ color: #ffffff;
+ background-color: #d9534f;
+ border-color: #d43f3a;
}
-legend small {
- font-size: 15px;
- color: #999999;
+.btn-danger:hover,
+.btn-danger:focus,
+.btn-danger:active,
+.btn-danger.active,
+.open .dropdown-toggle.btn-danger {
+ color: #ffffff;
+ background-color: #d2322d;
+ border-color: #ac2925;
}
-label,
-input,
-button,
-select,
-textarea {
- font-size: 14px;
- font-weight: normal;
- line-height: 20px;
+.btn-danger:active,
+.btn-danger.active,
+.open .dropdown-toggle.btn-danger {
+ background-image: none;
}
-input,
-button,
-select,
-textarea {
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+ background-color: #d9534f;
+ border-color: #d43f3a;
}
-label {
- display: block;
- margin-bottom: 5px;
+.btn-success {
+ color: #ffffff;
+ background-color: #5cb85c;
+ border-color: #4cae4c;
}
-select,
-textarea,
-input[type="text"],
-input[type="password"],
-input[type="datetime"],
-input[type="datetime-local"],
-input[type="date"],
-input[type="month"],
-input[type="time"],
-input[type="week"],
-input[type="number"],
-input[type="email"],
-input[type="url"],
-input[type="search"],
-input[type="tel"],
-input[type="color"],
-.uneditable-input {
- display: inline-block;
- height: 20px;
- padding: 4px 6px;
- margin-bottom: 10px;
- font-size: 14px;
- line-height: 20px;
- color: #555555;
- vertical-align: middle;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
+.btn-success:hover,
+.btn-success:focus,
+.btn-success:active,
+.btn-success.active,
+.open .dropdown-toggle.btn-success {
+ color: #ffffff;
+ background-color: #47a447;
+ border-color: #398439;
}
-input,
-textarea,
-.uneditable-input {
- width: 206px;
+.btn-success:active,
+.btn-success.active,
+.open .dropdown-toggle.btn-success {
+ background-image: none;
}
-textarea {
- height: auto;
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+ background-color: #5cb85c;
+ border-color: #4cae4c;
}
-textarea,
-input[type="text"],
-input[type="password"],
-input[type="datetime"],
-input[type="datetime-local"],
-input[type="date"],
-input[type="month"],
-input[type="time"],
-input[type="week"],
-input[type="number"],
-input[type="email"],
-input[type="url"],
-input[type="search"],
-input[type="tel"],
-input[type="color"],
-.uneditable-input {
- background-color: #ffffff;
- border: 1px solid #cccccc;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
- -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
- -o-transition: border linear 0.2s, box-shadow linear 0.2s;
- transition: border linear 0.2s, box-shadow linear 0.2s;
-}
-
-textarea:focus,
-input[type="text"]:focus,
-input[type="password"]:focus,
-input[type="datetime"]:focus,
-input[type="datetime-local"]:focus,
-input[type="date"]:focus,
-input[type="month"]:focus,
-input[type="time"]:focus,
-input[type="week"]:focus,
-input[type="number"]:focus,
-input[type="email"]:focus,
-input[type="url"]:focus,
-input[type="search"]:focus,
-input[type="tel"]:focus,
-input[type="color"]:focus,
-.uneditable-input:focus {
- border-color: rgba(82, 168, 236, 0.8);
- outline: 0;
- outline: thin dotted \9;
- /* IE6-9 */
-
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+.btn-info {
+ color: #ffffff;
+ background-color: #5bc0de;
+ border-color: #46b8da;
}
-input[type="radio"],
-input[type="checkbox"] {
- margin: 4px 0 0;
- margin-top: 1px \9;
- *margin-top: 0;
- line-height: normal;
+.btn-info:hover,
+.btn-info:focus,
+.btn-info:active,
+.btn-info.active,
+.open .dropdown-toggle.btn-info {
+ color: #ffffff;
+ background-color: #39b3d7;
+ border-color: #269abc;
}
-input[type="file"],
-input[type="image"],
-input[type="submit"],
-input[type="reset"],
-input[type="button"],
-input[type="radio"],
-input[type="checkbox"] {
- width: auto;
+.btn-info:active,
+.btn-info.active,
+.open .dropdown-toggle.btn-info {
+ background-image: none;
}
-select,
-input[type="file"] {
- height: 30px;
- /* In IE7, the height of the select element cannot be changed by height, only font-size */
-
- *margin-top: 4px;
- /* For IE7, add top margin to align select with labels */
-
- line-height: 30px;
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+ background-color: #5bc0de;
+ border-color: #46b8da;
}
-select {
- width: 220px;
- background-color: #ffffff;
- border: 1px solid #cccccc;
+.btn-link {
+ font-weight: normal;
+ color: #428bca;
+ cursor: pointer;
+ border-radius: 0;
}
-select[multiple],
-select[size] {
- height: auto;
+.btn-link,
+.btn-link:active,
+.btn-link[disabled],
+fieldset[disabled] .btn-link {
+ background-color: transparent;
+ -webkit-box-shadow: none;
+ box-shadow: none;
}
-select:focus,
-input[type="file"]:focus,
-input[type="radio"]:focus,
-input[type="checkbox"]:focus {
- outline: thin dotted #333;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
+.btn-link,
+.btn-link:hover,
+.btn-link:focus,
+.btn-link:active {
+ border-color: transparent;
+}
+
+.btn-link:hover,
+.btn-link:focus {
+ color: #2a6496;
+ text-decoration: underline;
+ background-color: transparent;
}
-.uneditable-input,
-.uneditable-textarea {
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
color: #999999;
- cursor: not-allowed;
- background-color: #fcfcfc;
- border-color: #cccccc;
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
- -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
- box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ text-decoration: none;
}
-.uneditable-input {
- overflow: hidden;
- white-space: nowrap;
+.btn-lg {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
}
-.uneditable-textarea {
- width: auto;
- height: auto;
+.btn-sm,
+.btn-xs {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
}
-input:-moz-placeholder,
-textarea:-moz-placeholder {
- color: #999999;
+.btn-xs {
+ padding: 1px 5px;
}
-input:-ms-input-placeholder,
-textarea:-ms-input-placeholder {
- color: #999999;
+.btn-block {
+ display: block;
+ width: 100%;
+ padding-right: 0;
+ padding-left: 0;
}
-input::-webkit-input-placeholder,
-textarea::-webkit-input-placeholder {
- color: #999999;
+.btn-block + .btn-block {
+ margin-top: 5px;
}
-.radio,
-.checkbox {
- min-height: 20px;
- padding-left: 20px;
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
}
-.radio input[type="radio"],
-.checkbox input[type="checkbox"] {
- float: left;
- margin-left: -20px;
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity 0.15s linear;
+ transition: opacity 0.15s linear;
}
-.controls > .radio:first-child,
-.controls > .checkbox:first-child {
- padding-top: 5px;
+.fade.in {
+ opacity: 1;
}
-.radio.inline,
-.checkbox.inline {
- display: inline-block;
- padding-top: 5px;
- margin-bottom: 0;
- vertical-align: middle;
+.collapse {
+ display: none;
}
-.radio.inline + .radio.inline,
-.checkbox.inline + .checkbox.inline {
- margin-left: 10px;
+.collapse.in {
+ display: block;
}
-.input-mini {
- width: 60px;
+.collapsing {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition: height 0.35s ease;
+ transition: height 0.35s ease;
}
-.input-small {
- width: 90px;
+@font-face {
+ font-family: 'Glyphicons Halflings';
+ src: url('../fonts/glyphicons-halflings-regular.eot');
+ src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
}
-.input-medium {
- width: 150px;
+.glyphicon {
+ position: relative;
+ top: 1px;
+ display: inline-block;
+ font-family: 'Glyphicons Halflings';
+ -webkit-font-smoothing: antialiased;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
}
-.input-large {
- width: 210px;
+.glyphicon-asterisk:before {
+ content: "\2a";
}
-.input-xlarge {
- width: 270px;
+.glyphicon-plus:before {
+ content: "\2b";
}
-.input-xxlarge {
- width: 530px;
+.glyphicon-euro:before {
+ content: "\20ac";
}
-input[class*="span"],
-select[class*="span"],
-textarea[class*="span"],
-.uneditable-input[class*="span"],
-.row-fluid input[class*="span"],
-.row-fluid select[class*="span"],
-.row-fluid textarea[class*="span"],
-.row-fluid .uneditable-input[class*="span"] {
- float: none;
- margin-left: 0;
+.glyphicon-minus:before {
+ content: "\2212";
}
-.input-append input[class*="span"],
-.input-append .uneditable-input[class*="span"],
-.input-prepend input[class*="span"],
-.input-prepend .uneditable-input[class*="span"],
-.row-fluid input[class*="span"],
-.row-fluid select[class*="span"],
-.row-fluid textarea[class*="span"],
-.row-fluid .uneditable-input[class*="span"],
-.row-fluid .input-prepend [class*="span"],
-.row-fluid .input-append [class*="span"] {
- display: inline-block;
+.glyphicon-cloud:before {
+ content: "\2601";
}
-input,
-textarea,
-.uneditable-input {
- margin-left: 0;
+.glyphicon-envelope:before {
+ content: "\2709";
}
-.controls-row [class*="span"] + [class*="span"] {
- margin-left: 20px;
+.glyphicon-pencil:before {
+ content: "\270f";
}
-input.span12,
-textarea.span12,
-.uneditable-input.span12 {
- width: 926px;
+.glyphicon-glass:before {
+ content: "\e001";
}
-input.span11,
-textarea.span11,
-.uneditable-input.span11 {
- width: 846px;
+.glyphicon-music:before {
+ content: "\e002";
}
-input.span10,
-textarea.span10,
-.uneditable-input.span10 {
- width: 766px;
+.glyphicon-search:before {
+ content: "\e003";
}
-input.span9,
-textarea.span9,
-.uneditable-input.span9 {
- width: 686px;
+.glyphicon-heart:before {
+ content: "\e005";
}
-input.span8,
-textarea.span8,
-.uneditable-input.span8 {
- width: 606px;
+.glyphicon-star:before {
+ content: "\e006";
}
-input.span7,
-textarea.span7,
-.uneditable-input.span7 {
- width: 526px;
+.glyphicon-star-empty:before {
+ content: "\e007";
}
-input.span6,
-textarea.span6,
-.uneditable-input.span6 {
- width: 446px;
+.glyphicon-user:before {
+ content: "\e008";
}
-input.span5,
-textarea.span5,
-.uneditable-input.span5 {
- width: 366px;
+.glyphicon-film:before {
+ content: "\e009";
}
-input.span4,
-textarea.span4,
-.uneditable-input.span4 {
- width: 286px;
+.glyphicon-th-large:before {
+ content: "\e010";
}
-input.span3,
-textarea.span3,
-.uneditable-input.span3 {
- width: 206px;
+.glyphicon-th:before {
+ content: "\e011";
}
-input.span2,
-textarea.span2,
-.uneditable-input.span2 {
- width: 126px;
+.glyphicon-th-list:before {
+ content: "\e012";
}
-input.span1,
-textarea.span1,
-.uneditable-input.span1 {
- width: 46px;
+.glyphicon-ok:before {
+ content: "\e013";
}
-.controls-row {
- *zoom: 1;
+.glyphicon-remove:before {
+ content: "\e014";
}
-.controls-row:before,
-.controls-row:after {
- display: table;
- line-height: 0;
- content: "";
+.glyphicon-zoom-in:before {
+ content: "\e015";
}
-.controls-row:after {
- clear: both;
+.glyphicon-zoom-out:before {
+ content: "\e016";
}
-.controls-row [class*="span"],
-.row-fluid .controls-row [class*="span"] {
- float: left;
+.glyphicon-off:before {
+ content: "\e017";
}
-.controls-row .checkbox[class*="span"],
-.controls-row .radio[class*="span"] {
- padding-top: 5px;
+.glyphicon-signal:before {
+ content: "\e018";
}
-input[disabled],
-select[disabled],
-textarea[disabled],
-input[readonly],
-select[readonly],
-textarea[readonly] {
- cursor: not-allowed;
- background-color: #eeeeee;
+.glyphicon-cog:before {
+ content: "\e019";
}
-input[type="radio"][disabled],
-input[type="checkbox"][disabled],
-input[type="radio"][readonly],
-input[type="checkbox"][readonly] {
- background-color: transparent;
+.glyphicon-trash:before {
+ content: "\e020";
}
-.control-group.warning .control-label,
-.control-group.warning .help-block,
-.control-group.warning .help-inline {
- color: #c09853;
+.glyphicon-home:before {
+ content: "\e021";
}
-.control-group.warning .checkbox,
-.control-group.warning .radio,
-.control-group.warning input,
-.control-group.warning select,
-.control-group.warning textarea {
- color: #c09853;
+.glyphicon-file:before {
+ content: "\e022";
}
-.control-group.warning input,
-.control-group.warning select,
-.control-group.warning textarea {
- border-color: #c09853;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+.glyphicon-time:before {
+ content: "\e023";
}
-.control-group.warning input:focus,
-.control-group.warning select:focus,
-.control-group.warning textarea:focus {
- border-color: #a47e3c;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+.glyphicon-road:before {
+ content: "\e024";
}
-.control-group.warning .input-prepend .add-on,
-.control-group.warning .input-append .add-on {
- color: #c09853;
- background-color: #fcf8e3;
- border-color: #c09853;
+.glyphicon-download-alt:before {
+ content: "\e025";
}
-.control-group.error .control-label,
-.control-group.error .help-block,
-.control-group.error .help-inline {
- color: #b94a48;
+.glyphicon-download:before {
+ content: "\e026";
}
-.control-group.error .checkbox,
-.control-group.error .radio,
-.control-group.error input,
-.control-group.error select,
-.control-group.error textarea {
- color: #b94a48;
+.glyphicon-upload:before {
+ content: "\e027";
}
-.control-group.error input,
-.control-group.error select,
-.control-group.error textarea {
- border-color: #b94a48;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+.glyphicon-inbox:before {
+ content: "\e028";
}
-.control-group.error input:focus,
-.control-group.error select:focus,
-.control-group.error textarea:focus {
- border-color: #953b39;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+.glyphicon-play-circle:before {
+ content: "\e029";
}
-.control-group.error .input-prepend .add-on,
-.control-group.error .input-append .add-on {
- color: #b94a48;
- background-color: #f2dede;
- border-color: #b94a48;
+.glyphicon-repeat:before {
+ content: "\e030";
}
-.control-group.success .control-label,
-.control-group.success .help-block,
-.control-group.success .help-inline {
- color: #468847;
+.glyphicon-refresh:before {
+ content: "\e031";
}
-.control-group.success .checkbox,
-.control-group.success .radio,
-.control-group.success input,
-.control-group.success select,
-.control-group.success textarea {
- color: #468847;
+.glyphicon-list-alt:before {
+ content: "\e032";
}
-.control-group.success input,
-.control-group.success select,
-.control-group.success textarea {
- border-color: #468847;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+.glyphicon-flag:before {
+ content: "\e034";
}
-.control-group.success input:focus,
-.control-group.success select:focus,
-.control-group.success textarea:focus {
- border-color: #356635;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+.glyphicon-headphones:before {
+ content: "\e035";
}
-.control-group.success .input-prepend .add-on,
-.control-group.success .input-append .add-on {
- color: #468847;
- background-color: #dff0d8;
- border-color: #468847;
+.glyphicon-volume-off:before {
+ content: "\e036";
}
-.control-group.info .control-label,
-.control-group.info .help-block,
-.control-group.info .help-inline {
- color: #3a87ad;
+.glyphicon-volume-down:before {
+ content: "\e037";
}
-.control-group.info .checkbox,
-.control-group.info .radio,
-.control-group.info input,
-.control-group.info select,
-.control-group.info textarea {
- color: #3a87ad;
+.glyphicon-volume-up:before {
+ content: "\e038";
}
-.control-group.info input,
-.control-group.info select,
-.control-group.info textarea {
- border-color: #3a87ad;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+.glyphicon-qrcode:before {
+ content: "\e039";
}
-.control-group.info input:focus,
-.control-group.info select:focus,
-.control-group.info textarea:focus {
- border-color: #2d6987;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
+.glyphicon-barcode:before {
+ content: "\e040";
}
-.control-group.info .input-prepend .add-on,
-.control-group.info .input-append .add-on {
- color: #3a87ad;
- background-color: #d9edf7;
- border-color: #3a87ad;
+.glyphicon-tag:before {
+ content: "\e041";
}
-input:focus:invalid,
-textarea:focus:invalid,
-select:focus:invalid {
- color: #b94a48;
- border-color: #ee5f5b;
+.glyphicon-tags:before {
+ content: "\e042";
}
-input:focus:invalid:focus,
-textarea:focus:invalid:focus,
-select:focus:invalid:focus {
- border-color: #e9322d;
- -webkit-box-shadow: 0 0 6px #f8b9b7;
- -moz-box-shadow: 0 0 6px #f8b9b7;
- box-shadow: 0 0 6px #f8b9b7;
+.glyphicon-book:before {
+ content: "\e043";
}
-.form-actions {
- padding: 19px 20px 20px;
- margin-top: 20px;
- margin-bottom: 20px;
- background-color: #f5f5f5;
- border-top: 1px solid #e5e5e5;
- *zoom: 1;
+.glyphicon-print:before {
+ content: "\e045";
}
-.form-actions:before,
-.form-actions:after {
- display: table;
- line-height: 0;
- content: "";
+.glyphicon-font:before {
+ content: "\e047";
}
-.form-actions:after {
- clear: both;
+.glyphicon-bold:before {
+ content: "\e048";
}
-.help-block,
-.help-inline {
- color: #595959;
+.glyphicon-italic:before {
+ content: "\e049";
}
-.help-block {
- display: block;
- margin-bottom: 10px;
+.glyphicon-text-height:before {
+ content: "\e050";
}
-.help-inline {
- display: inline-block;
- *display: inline;
- padding-left: 5px;
- vertical-align: middle;
- *zoom: 1;
+.glyphicon-text-width:before {
+ content: "\e051";
}
-.input-append,
-.input-prepend {
- display: inline-block;
- margin-bottom: 10px;
- font-size: 0;
- white-space: nowrap;
- vertical-align: middle;
+.glyphicon-align-left:before {
+ content: "\e052";
}
-.input-append input,
-.input-prepend input,
-.input-append select,
-.input-prepend select,
-.input-append .uneditable-input,
-.input-prepend .uneditable-input,
-.input-append .dropdown-menu,
-.input-prepend .dropdown-menu,
-.input-append .popover,
-.input-prepend .popover {
- font-size: 14px;
+.glyphicon-align-center:before {
+ content: "\e053";
}
-.input-append input,
-.input-prepend input,
-.input-append select,
-.input-prepend select,
-.input-append .uneditable-input,
-.input-prepend .uneditable-input {
- position: relative;
- margin-bottom: 0;
- *margin-left: 0;
- vertical-align: top;
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
+.glyphicon-align-right:before {
+ content: "\e054";
}
-.input-append input:focus,
-.input-prepend input:focus,
-.input-append select:focus,
-.input-prepend select:focus,
-.input-append .uneditable-input:focus,
-.input-prepend .uneditable-input:focus {
- z-index: 2;
+.glyphicon-align-justify:before {
+ content: "\e055";
}
-.input-append .add-on,
-.input-prepend .add-on {
- display: inline-block;
- width: auto;
- height: 20px;
- min-width: 16px;
- padding: 4px 5px;
- font-size: 14px;
- font-weight: normal;
- line-height: 20px;
- text-align: center;
- text-shadow: 0 1px 0 #ffffff;
- background-color: #eeeeee;
- border: 1px solid #ccc;
+.glyphicon-list:before {
+ content: "\e056";
}
-.input-append .add-on,
-.input-prepend .add-on,
-.input-append .btn,
-.input-prepend .btn,
-.input-append .btn-group > .dropdown-toggle,
-.input-prepend .btn-group > .dropdown-toggle {
- vertical-align: top;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
+.glyphicon-indent-left:before {
+ content: "\e057";
}
-.input-append .active,
-.input-prepend .active {
- background-color: #a9dba9;
- border-color: #46a546;
+.glyphicon-indent-right:before {
+ content: "\e058";
}
-.input-prepend .add-on,
-.input-prepend .btn {
- margin-right: -1px;
+.glyphicon-facetime-video:before {
+ content: "\e059";
}
-.input-prepend .add-on:first-child,
-.input-prepend .btn:first-child {
- -webkit-border-radius: 4px 0 0 4px;
- -moz-border-radius: 4px 0 0 4px;
- border-radius: 4px 0 0 4px;
+.glyphicon-picture:before {
+ content: "\e060";
}
-.input-append input,
-.input-append select,
-.input-append .uneditable-input {
- -webkit-border-radius: 4px 0 0 4px;
- -moz-border-radius: 4px 0 0 4px;
- border-radius: 4px 0 0 4px;
+.glyphicon-map-marker:before {
+ content: "\e062";
}
-.input-append input + .btn-group .btn:last-child,
-.input-append select + .btn-group .btn:last-child,
-.input-append .uneditable-input + .btn-group .btn:last-child {
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
+.glyphicon-adjust:before {
+ content: "\e063";
}
-.input-append .add-on,
-.input-append .btn,
-.input-append .btn-group {
- margin-left: -1px;
+.glyphicon-tint:before {
+ content: "\e064";
}
-.input-append .add-on:last-child,
-.input-append .btn:last-child,
-.input-append .btn-group:last-child > .dropdown-toggle {
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
+.glyphicon-edit:before {
+ content: "\e065";
}
-.input-prepend.input-append input,
-.input-prepend.input-append select,
-.input-prepend.input-append .uneditable-input {
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
+.glyphicon-share:before {
+ content: "\e066";
}
-.input-prepend.input-append input + .btn-group .btn,
-.input-prepend.input-append select + .btn-group .btn,
-.input-prepend.input-append .uneditable-input + .btn-group .btn {
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
+.glyphicon-check:before {
+ content: "\e067";
}
-.input-prepend.input-append .add-on:first-child,
-.input-prepend.input-append .btn:first-child {
- margin-right: -1px;
- -webkit-border-radius: 4px 0 0 4px;
- -moz-border-radius: 4px 0 0 4px;
- border-radius: 4px 0 0 4px;
+.glyphicon-move:before {
+ content: "\e068";
}
-.input-prepend.input-append .add-on:last-child,
-.input-prepend.input-append .btn:last-child {
- margin-left: -1px;
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
+.glyphicon-step-backward:before {
+ content: "\e069";
}
-.input-prepend.input-append .btn-group:first-child {
- margin-left: 0;
+.glyphicon-fast-backward:before {
+ content: "\e070";
}
-input.search-query {
- padding-right: 14px;
- padding-right: 4px \9;
- padding-left: 14px;
- padding-left: 4px \9;
- /* IE7-8 doesn't have border-radius, so don't indent the padding */
+.glyphicon-backward:before {
+ content: "\e071";
+}
- margin-bottom: 0;
- -webkit-border-radius: 15px;
- -moz-border-radius: 15px;
- border-radius: 15px;
-}
-
-/* Allow for input prepend/append in search forms */
-
-.form-search .input-append .search-query,
-.form-search .input-prepend .search-query {
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
-}
-
-.form-search .input-append .search-query {
- -webkit-border-radius: 14px 0 0 14px;
- -moz-border-radius: 14px 0 0 14px;
- border-radius: 14px 0 0 14px;
-}
-
-.form-search .input-append .btn {
- -webkit-border-radius: 0 14px 14px 0;
- -moz-border-radius: 0 14px 14px 0;
- border-radius: 0 14px 14px 0;
-}
-
-.form-search .input-prepend .search-query {
- -webkit-border-radius: 0 14px 14px 0;
- -moz-border-radius: 0 14px 14px 0;
- border-radius: 0 14px 14px 0;
-}
-
-.form-search .input-prepend .btn {
- -webkit-border-radius: 14px 0 0 14px;
- -moz-border-radius: 14px 0 0 14px;
- border-radius: 14px 0 0 14px;
-}
-
-.form-search input,
-.form-inline input,
-.form-horizontal input,
-.form-search textarea,
-.form-inline textarea,
-.form-horizontal textarea,
-.form-search select,
-.form-inline select,
-.form-horizontal select,
-.form-search .help-inline,
-.form-inline .help-inline,
-.form-horizontal .help-inline,
-.form-search .uneditable-input,
-.form-inline .uneditable-input,
-.form-horizontal .uneditable-input,
-.form-search .input-prepend,
-.form-inline .input-prepend,
-.form-horizontal .input-prepend,
-.form-search .input-append,
-.form-inline .input-append,
-.form-horizontal .input-append {
- display: inline-block;
- *display: inline;
- margin-bottom: 0;
- vertical-align: middle;
- *zoom: 1;
+.glyphicon-play:before {
+ content: "\e072";
}
-.form-search .hide,
-.form-inline .hide,
-.form-horizontal .hide {
- display: none;
+.glyphicon-pause:before {
+ content: "\e073";
}
-.form-search label,
-.form-inline label,
-.form-search .btn-group,
-.form-inline .btn-group {
- display: inline-block;
+.glyphicon-stop:before {
+ content: "\e074";
}
-.form-search .input-append,
-.form-inline .input-append,
-.form-search .input-prepend,
-.form-inline .input-prepend {
- margin-bottom: 0;
+.glyphicon-forward:before {
+ content: "\e075";
}
-.form-search .radio,
-.form-search .checkbox,
-.form-inline .radio,
-.form-inline .checkbox {
- padding-left: 0;
- margin-bottom: 0;
- vertical-align: middle;
+.glyphicon-fast-forward:before {
+ content: "\e076";
}
-.form-search .radio input[type="radio"],
-.form-search .checkbox input[type="checkbox"],
-.form-inline .radio input[type="radio"],
-.form-inline .checkbox input[type="checkbox"] {
- float: left;
- margin-right: 3px;
- margin-left: 0;
+.glyphicon-step-forward:before {
+ content: "\e077";
}
-.control-group {
- margin-bottom: 10px;
+.glyphicon-eject:before {
+ content: "\e078";
}
-legend + .control-group {
- margin-top: 20px;
- -webkit-margin-top-collapse: separate;
+.glyphicon-chevron-left:before {
+ content: "\e079";
}
-.form-horizontal .control-group {
- margin-bottom: 20px;
- *zoom: 1;
+.glyphicon-chevron-right:before {
+ content: "\e080";
}
-.form-horizontal .control-group:before,
-.form-horizontal .control-group:after {
- display: table;
- line-height: 0;
- content: "";
+.glyphicon-plus-sign:before {
+ content: "\e081";
}
-.form-horizontal .control-group:after {
- clear: both;
+.glyphicon-minus-sign:before {
+ content: "\e082";
}
-.form-horizontal .control-label {
- float: left;
- width: 160px;
- padding-top: 5px;
- text-align: right;
+.glyphicon-remove-sign:before {
+ content: "\e083";
}
-.form-horizontal .controls {
- *display: inline-block;
- *padding-left: 20px;
- margin-left: 180px;
- *margin-left: 0;
+.glyphicon-ok-sign:before {
+ content: "\e084";
}
-.form-horizontal .controls:first-child {
- *padding-left: 180px;
+.glyphicon-question-sign:before {
+ content: "\e085";
}
-.form-horizontal .help-block {
- margin-bottom: 0;
+.glyphicon-info-sign:before {
+ content: "\e086";
}
-.form-horizontal input + .help-block,
-.form-horizontal select + .help-block,
-.form-horizontal textarea + .help-block,
-.form-horizontal .uneditable-input + .help-block,
-.form-horizontal .input-prepend + .help-block,
-.form-horizontal .input-append + .help-block {
- margin-top: 10px;
+.glyphicon-screenshot:before {
+ content: "\e087";
}
-.form-horizontal .form-actions {
- padding-left: 180px;
+.glyphicon-remove-circle:before {
+ content: "\e088";
}
-table {
- max-width: 100%;
- background-color: transparent;
- border-collapse: collapse;
- border-spacing: 0;
+.glyphicon-ok-circle:before {
+ content: "\e089";
}
-.table {
- width: 100%;
- margin-bottom: 20px;
+.glyphicon-ban-circle:before {
+ content: "\e090";
}
-.table th,
-.table td {
- padding: 8px;
- line-height: 20px;
- text-align: left;
- vertical-align: top;
- border-top: 1px solid #dddddd;
+.glyphicon-arrow-left:before {
+ content: "\e091";
}
-.table th {
- font-weight: bold;
+.glyphicon-arrow-right:before {
+ content: "\e092";
}
-.table thead th {
- vertical-align: bottom;
+.glyphicon-arrow-up:before {
+ content: "\e093";
}
-.table caption + thead tr:first-child th,
-.table caption + thead tr:first-child td,
-.table colgroup + thead tr:first-child th,
-.table colgroup + thead tr:first-child td,
-.table thead:first-child tr:first-child th,
-.table thead:first-child tr:first-child td {
- border-top: 0;
+.glyphicon-arrow-down:before {
+ content: "\e094";
}
-.table tbody + tbody {
- border-top: 2px solid #dddddd;
+.glyphicon-share-alt:before {
+ content: "\e095";
}
-.table .table {
- background-color: #ffffff;
+.glyphicon-resize-full:before {
+ content: "\e096";
}
-.table-condensed th,
-.table-condensed td {
- padding: 4px 5px;
+.glyphicon-resize-small:before {
+ content: "\e097";
}
-.table-bordered {
- border: 1px solid #dddddd;
- border-collapse: separate;
- *border-collapse: collapse;
- border-left: 0;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
+.glyphicon-exclamation-sign:before {
+ content: "\e101";
}
-.table-bordered th,
-.table-bordered td {
- border-left: 1px solid #dddddd;
+.glyphicon-gift:before {
+ content: "\e102";
}
-.table-bordered caption + thead tr:first-child th,
-.table-bordered caption + tbody tr:first-child th,
-.table-bordered caption + tbody tr:first-child td,
-.table-bordered colgroup + thead tr:first-child th,
-.table-bordered colgroup + tbody tr:first-child th,
-.table-bordered colgroup + tbody tr:first-child td,
-.table-bordered thead:first-child tr:first-child th,
-.table-bordered tbody:first-child tr:first-child th,
-.table-bordered tbody:first-child tr:first-child td {
- border-top: 0;
+.glyphicon-leaf:before {
+ content: "\e103";
}
-.table-bordered thead:first-child tr:first-child > th:first-child,
-.table-bordered tbody:first-child tr:first-child > td:first-child,
-.table-bordered tbody:first-child tr:first-child > th:first-child {
- -webkit-border-top-left-radius: 4px;
- border-top-left-radius: 4px;
- -moz-border-radius-topleft: 4px;
+.glyphicon-eye-open:before {
+ content: "\e105";
}
-.table-bordered thead:first-child tr:first-child > th:last-child,
-.table-bordered tbody:first-child tr:first-child > td:last-child,
-.table-bordered tbody:first-child tr:first-child > th:last-child {
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
- -moz-border-radius-topright: 4px;
+.glyphicon-eye-close:before {
+ content: "\e106";
}
-.table-bordered thead:last-child tr:last-child > th:first-child,
-.table-bordered tbody:last-child tr:last-child > td:first-child,
-.table-bordered tbody:last-child tr:last-child > th:first-child,
-.table-bordered tfoot:last-child tr:last-child > td:first-child,
-.table-bordered tfoot:last-child tr:last-child > th:first-child {
- -webkit-border-bottom-left-radius: 4px;
- border-bottom-left-radius: 4px;
- -moz-border-radius-bottomleft: 4px;
+.glyphicon-warning-sign:before {
+ content: "\e107";
}
-.table-bordered thead:last-child tr:last-child > th:last-child,
-.table-bordered tbody:last-child tr:last-child > td:last-child,
-.table-bordered tbody:last-child tr:last-child > th:last-child,
-.table-bordered tfoot:last-child tr:last-child > td:last-child,
-.table-bordered tfoot:last-child tr:last-child > th:last-child {
- -webkit-border-bottom-right-radius: 4px;
- border-bottom-right-radius: 4px;
- -moz-border-radius-bottomright: 4px;
+.glyphicon-plane:before {
+ content: "\e108";
}
-.table-bordered tfoot + tbody:last-child tr:last-child td:first-child {
- -webkit-border-bottom-left-radius: 0;
- border-bottom-left-radius: 0;
- -moz-border-radius-bottomleft: 0;
+.glyphicon-random:before {
+ content: "\e110";
}
-.table-bordered tfoot + tbody:last-child tr:last-child td:last-child {
- -webkit-border-bottom-right-radius: 0;
- border-bottom-right-radius: 0;
- -moz-border-radius-bottomright: 0;
+.glyphicon-comment:before {
+ content: "\e111";
}
-.table-bordered caption + thead tr:first-child th:first-child,
-.table-bordered caption + tbody tr:first-child td:first-child,
-.table-bordered colgroup + thead tr:first-child th:first-child,
-.table-bordered colgroup + tbody tr:first-child td:first-child {
- -webkit-border-top-left-radius: 4px;
- border-top-left-radius: 4px;
- -moz-border-radius-topleft: 4px;
+.glyphicon-magnet:before {
+ content: "\e112";
}
-.table-bordered caption + thead tr:first-child th:last-child,
-.table-bordered caption + tbody tr:first-child td:last-child,
-.table-bordered colgroup + thead tr:first-child th:last-child,
-.table-bordered colgroup + tbody tr:first-child td:last-child {
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
- -moz-border-radius-topright: 4px;
+.glyphicon-chevron-up:before {
+ content: "\e113";
}
-.table-striped tbody > tr:nth-child(odd) > td,
-.table-striped tbody > tr:nth-child(odd) > th {
- background-color: #f9f9f9;
+.glyphicon-chevron-down:before {
+ content: "\e114";
}
-.table-hover tbody tr:hover > td,
-.table-hover tbody tr:hover > th {
- background-color: #f5f5f5;
+.glyphicon-retweet:before {
+ content: "\e115";
}
-table td[class*="span"],
-table th[class*="span"],
-.row-fluid table td[class*="span"],
-.row-fluid table th[class*="span"] {
- display: table-cell;
- float: none;
- margin-left: 0;
+.glyphicon-shopping-cart:before {
+ content: "\e116";
}
-.table td.span1,
-.table th.span1 {
- float: none;
- width: 44px;
- margin-left: 0;
+.glyphicon-folder-close:before {
+ content: "\e117";
}
-.table td.span2,
-.table th.span2 {
- float: none;
- width: 124px;
- margin-left: 0;
+.glyphicon-folder-open:before {
+ content: "\e118";
}
-.table td.span3,
-.table th.span3 {
- float: none;
- width: 204px;
- margin-left: 0;
+.glyphicon-resize-vertical:before {
+ content: "\e119";
}
-.table td.span4,
-.table th.span4 {
- float: none;
- width: 284px;
- margin-left: 0;
+.glyphicon-resize-horizontal:before {
+ content: "\e120";
}
-.table td.span5,
-.table th.span5 {
- float: none;
- width: 364px;
- margin-left: 0;
+.glyphicon-hdd:before {
+ content: "\e121";
}
-.table td.span6,
-.table th.span6 {
- float: none;
- width: 444px;
- margin-left: 0;
+.glyphicon-bullhorn:before {
+ content: "\e122";
}
-.table td.span7,
-.table th.span7 {
- float: none;
- width: 524px;
- margin-left: 0;
+.glyphicon-certificate:before {
+ content: "\e124";
}
-.table td.span8,
-.table th.span8 {
- float: none;
- width: 604px;
- margin-left: 0;
+.glyphicon-thumbs-up:before {
+ content: "\e125";
}
-.table td.span9,
-.table th.span9 {
- float: none;
- width: 684px;
- margin-left: 0;
+.glyphicon-thumbs-down:before {
+ content: "\e126";
}
-.table td.span10,
-.table th.span10 {
- float: none;
- width: 764px;
- margin-left: 0;
+.glyphicon-hand-right:before {
+ content: "\e127";
}
-.table td.span11,
-.table th.span11 {
- float: none;
- width: 844px;
- margin-left: 0;
+.glyphicon-hand-left:before {
+ content: "\e128";
}
-.table td.span12,
-.table th.span12 {
- float: none;
- width: 924px;
- margin-left: 0;
+.glyphicon-hand-up:before {
+ content: "\e129";
}
-.table tbody tr.success > td {
- background-color: #dff0d8;
+.glyphicon-hand-down:before {
+ content: "\e130";
}
-.table tbody tr.error > td {
- background-color: #f2dede;
+.glyphicon-circle-arrow-right:before {
+ content: "\e131";
}
-.table tbody tr.warning > td {
- background-color: #fcf8e3;
+.glyphicon-circle-arrow-left:before {
+ content: "\e132";
}
-.table tbody tr.info > td {
- background-color: #d9edf7;
+.glyphicon-circle-arrow-up:before {
+ content: "\e133";
}
-.table-hover tbody tr.success:hover > td {
- background-color: #d0e9c6;
+.glyphicon-circle-arrow-down:before {
+ content: "\e134";
}
-.table-hover tbody tr.error:hover > td {
- background-color: #ebcccc;
+.glyphicon-globe:before {
+ content: "\e135";
}
-.table-hover tbody tr.warning:hover > td {
- background-color: #faf2cc;
+.glyphicon-tasks:before {
+ content: "\e137";
}
-.table-hover tbody tr.info:hover > td {
- background-color: #c4e3f3;
+.glyphicon-filter:before {
+ content: "\e138";
}
-[class^="icon-"],
-[class*=" icon-"] {
- display: inline-block;
- width: 14px;
- height: 14px;
- margin-top: 1px;
- *margin-right: .3em;
- line-height: 14px;
- vertical-align: text-top;
- background-image: url("../img/glyphicons-halflings.png");
- background-position: 14px 14px;
- background-repeat: no-repeat;
+.glyphicon-fullscreen:before {
+ content: "\e140";
}
-/* White icons with optional class, or on hover/focus/active states of certain elements */
+.glyphicon-dashboard:before {
+ content: "\e141";
+}
-.icon-white,
-.nav-pills > .active > a > [class^="icon-"],
-.nav-pills > .active > a > [class*=" icon-"],
-.nav-list > .active > a > [class^="icon-"],
-.nav-list > .active > a > [class*=" icon-"],
-.navbar-inverse .nav > .active > a > [class^="icon-"],
-.navbar-inverse .nav > .active > a > [class*=" icon-"],
-.dropdown-menu > li > a:hover > [class^="icon-"],
-.dropdown-menu > li > a:focus > [class^="icon-"],
-.dropdown-menu > li > a:hover > [class*=" icon-"],
-.dropdown-menu > li > a:focus > [class*=" icon-"],
-.dropdown-menu > .active > a > [class^="icon-"],
-.dropdown-menu > .active > a > [class*=" icon-"],
-.dropdown-submenu:hover > a > [class^="icon-"],
-.dropdown-submenu:focus > a > [class^="icon-"],
-.dropdown-submenu:hover > a > [class*=" icon-"],
-.dropdown-submenu:focus > a > [class*=" icon-"] {
- background-image: url("../img/glyphicons-halflings-white.png");
+.glyphicon-heart-empty:before {
+ content: "\e143";
}
-.icon-glass {
- background-position: 0 0;
+.glyphicon-link:before {
+ content: "\e144";
}
-.icon-music {
- background-position: -24px 0;
+.glyphicon-phone:before {
+ content: "\e145";
}
-.icon-search {
- background-position: -48px 0;
+.glyphicon-usd:before {
+ content: "\e148";
}
-.icon-envelope {
- background-position: -72px 0;
+.glyphicon-gbp:before {
+ content: "\e149";
}
-.icon-heart {
- background-position: -96px 0;
+.glyphicon-sort:before {
+ content: "\e150";
}
-.icon-star {
- background-position: -120px 0;
+.glyphicon-sort-by-alphabet:before {
+ content: "\e151";
}
-.icon-star-empty {
- background-position: -144px 0;
+.glyphicon-sort-by-alphabet-alt:before {
+ content: "\e152";
}
-.icon-user {
- background-position: -168px 0;
+.glyphicon-sort-by-order:before {
+ content: "\e153";
}
-.icon-film {
- background-position: -192px 0;
+.glyphicon-sort-by-order-alt:before {
+ content: "\e154";
}
-.icon-th-large {
- background-position: -216px 0;
+.glyphicon-sort-by-attributes:before {
+ content: "\e155";
}
-.icon-th {
- background-position: -240px 0;
+.glyphicon-sort-by-attributes-alt:before {
+ content: "\e156";
}
-.icon-th-list {
- background-position: -264px 0;
+.glyphicon-unchecked:before {
+ content: "\e157";
}
-.icon-ok {
- background-position: -288px 0;
+.glyphicon-expand:before {
+ content: "\e158";
}
-.icon-remove {
- background-position: -312px 0;
+.glyphicon-collapse-down:before {
+ content: "\e159";
}
-.icon-zoom-in {
- background-position: -336px 0;
+.glyphicon-collapse-up:before {
+ content: "\e160";
}
-.icon-zoom-out {
- background-position: -360px 0;
+.glyphicon-log-in:before {
+ content: "\e161";
}
-.icon-off {
- background-position: -384px 0;
+.glyphicon-flash:before {
+ content: "\e162";
}
-.icon-signal {
- background-position: -408px 0;
+.glyphicon-log-out:before {
+ content: "\e163";
}
-.icon-cog {
- background-position: -432px 0;
+.glyphicon-new-window:before {
+ content: "\e164";
}
-.icon-trash {
- background-position: -456px 0;
+.glyphicon-record:before {
+ content: "\e165";
}
-.icon-home {
- background-position: 0 -24px;
+.glyphicon-save:before {
+ content: "\e166";
}
-.icon-file {
- background-position: -24px -24px;
+.glyphicon-open:before {
+ content: "\e167";
}
-.icon-time {
- background-position: -48px -24px;
+.glyphicon-saved:before {
+ content: "\e168";
}
-.icon-road {
- background-position: -72px -24px;
+.glyphicon-import:before {
+ content: "\e169";
}
-.icon-download-alt {
- background-position: -96px -24px;
+.glyphicon-export:before {
+ content: "\e170";
}
-.icon-download {
- background-position: -120px -24px;
+.glyphicon-send:before {
+ content: "\e171";
}
-.icon-upload {
- background-position: -144px -24px;
+.glyphicon-floppy-disk:before {
+ content: "\e172";
}
-.icon-inbox {
- background-position: -168px -24px;
+.glyphicon-floppy-saved:before {
+ content: "\e173";
}
-.icon-play-circle {
- background-position: -192px -24px;
+.glyphicon-floppy-remove:before {
+ content: "\e174";
}
-.icon-repeat {
- background-position: -216px -24px;
+.glyphicon-floppy-save:before {
+ content: "\e175";
}
-.icon-refresh {
- background-position: -240px -24px;
+.glyphicon-floppy-open:before {
+ content: "\e176";
}
-.icon-list-alt {
- background-position: -264px -24px;
+.glyphicon-credit-card:before {
+ content: "\e177";
}
-.icon-lock {
- background-position: -287px -24px;
+.glyphicon-transfer:before {
+ content: "\e178";
}
-.icon-flag {
- background-position: -312px -24px;
+.glyphicon-cutlery:before {
+ content: "\e179";
}
-.icon-headphones {
- background-position: -336px -24px;
+.glyphicon-header:before {
+ content: "\e180";
}
-.icon-volume-off {
- background-position: -360px -24px;
+.glyphicon-compressed:before {
+ content: "\e181";
}
-.icon-volume-down {
- background-position: -384px -24px;
+.glyphicon-earphone:before {
+ content: "\e182";
}
-.icon-volume-up {
- background-position: -408px -24px;
+.glyphicon-phone-alt:before {
+ content: "\e183";
}
-.icon-qrcode {
- background-position: -432px -24px;
+.glyphicon-tower:before {
+ content: "\e184";
}
-.icon-barcode {
- background-position: -456px -24px;
+.glyphicon-stats:before {
+ content: "\e185";
}
-.icon-tag {
- background-position: 0 -48px;
+.glyphicon-sd-video:before {
+ content: "\e186";
}
-.icon-tags {
- background-position: -25px -48px;
+.glyphicon-hd-video:before {
+ content: "\e187";
}
-.icon-book {
- background-position: -48px -48px;
+.glyphicon-subtitles:before {
+ content: "\e188";
}
-.icon-bookmark {
- background-position: -72px -48px;
+.glyphicon-sound-stereo:before {
+ content: "\e189";
}
-.icon-print {
- background-position: -96px -48px;
+.glyphicon-sound-dolby:before {
+ content: "\e190";
}
-.icon-camera {
- background-position: -120px -48px;
+.glyphicon-sound-5-1:before {
+ content: "\e191";
}
-.icon-font {
- background-position: -144px -48px;
+.glyphicon-sound-6-1:before {
+ content: "\e192";
}
-.icon-bold {
- background-position: -167px -48px;
+.glyphicon-sound-7-1:before {
+ content: "\e193";
}
-.icon-italic {
- background-position: -192px -48px;
+.glyphicon-copyright-mark:before {
+ content: "\e194";
}
-.icon-text-height {
- background-position: -216px -48px;
+.glyphicon-registration-mark:before {
+ content: "\e195";
}
-.icon-text-width {
- background-position: -240px -48px;
+.glyphicon-cloud-download:before {
+ content: "\e197";
}
-.icon-align-left {
- background-position: -264px -48px;
+.glyphicon-cloud-upload:before {
+ content: "\e198";
}
-.icon-align-center {
- background-position: -288px -48px;
+.glyphicon-tree-conifer:before {
+ content: "\e199";
}
-.icon-align-right {
- background-position: -312px -48px;
+.glyphicon-tree-deciduous:before {
+ content: "\e200";
}
-.icon-align-justify {
- background-position: -336px -48px;
+.glyphicon-briefcase:before {
+ content: "\1f4bc";
}
-.icon-list {
- background-position: -360px -48px;
+.glyphicon-calendar:before {
+ content: "\1f4c5";
}
-.icon-indent-left {
- background-position: -384px -48px;
+.glyphicon-pushpin:before {
+ content: "\1f4cc";
}
-.icon-indent-right {
- background-position: -408px -48px;
+.glyphicon-paperclip:before {
+ content: "\1f4ce";
}
-.icon-facetime-video {
- background-position: -432px -48px;
+.glyphicon-camera:before {
+ content: "\1f4f7";
}
-.icon-picture {
- background-position: -456px -48px;
+.glyphicon-lock:before {
+ content: "\1f512";
}
-.icon-pencil {
- background-position: 0 -72px;
+.glyphicon-bell:before {
+ content: "\1f514";
}
-.icon-map-marker {
- background-position: -24px -72px;
+.glyphicon-bookmark:before {
+ content: "\1f516";
}
-.icon-adjust {
- background-position: -48px -72px;
+.glyphicon-fire:before {
+ content: "\1f525";
}
-.icon-tint {
- background-position: -72px -72px;
+.glyphicon-wrench:before {
+ content: "\1f527";
}
-.icon-edit {
- background-position: -96px -72px;
+.caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-left: 2px;
+ vertical-align: middle;
+ border-top: 4px solid #000000;
+ border-right: 4px solid transparent;
+ border-bottom: 0 dotted;
+ border-left: 4px solid transparent;
+ content: "";
}
-.icon-share {
- background-position: -120px -72px;
+.dropdown {
+ position: relative;
}
-.icon-check {
- background-position: -144px -72px;
+.dropdown-toggle:focus {
+ outline: 0;
}
-.icon-move {
- background-position: -168px -72px;
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ font-size: 14px;
+ list-style: none;
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 4px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ background-clip: padding-box;
}
-.icon-step-backward {
- background-position: -192px -72px;
+.dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
}
-.icon-fast-backward {
- background-position: -216px -72px;
+.dropdown-menu .divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5;
}
-.icon-backward {
- background-position: -240px -72px;
+.dropdown-menu > li > a {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 1.428571429;
+ color: #333333;
+ white-space: nowrap;
}
-.icon-play {
- background-position: -264px -72px;
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: #428bca;
}
-.icon-pause {
- background-position: -288px -72px;
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: #428bca;
+ outline: 0;
}
-.icon-stop {
- background-position: -312px -72px;
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ color: #999999;
}
-.icon-forward {
- background-position: -336px -72px;
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ text-decoration: none;
+ cursor: not-allowed;
+ background-color: transparent;
+ background-image: none;
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
-.icon-fast-forward {
- background-position: -360px -72px;
+.open > .dropdown-menu {
+ display: block;
}
-.icon-step-forward {
- background-position: -384px -72px;
+.open > a {
+ outline: 0;
}
-.icon-eject {
- background-position: -408px -72px;
+.dropdown-header {
+ display: block;
+ padding: 3px 20px;
+ font-size: 12px;
+ line-height: 1.428571429;
+ color: #999999;
}
-.icon-chevron-left {
- background-position: -432px -72px;
+.dropdown-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 990;
}
-.icon-chevron-right {
- background-position: -456px -72px;
+.pull-right > .dropdown-menu {
+ right: 0;
+ left: auto;
}
-.icon-plus-sign {
- background-position: 0 -96px;
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+ border-top: 0 dotted;
+ border-bottom: 4px solid #000000;
+ content: "";
}
-.icon-minus-sign {
- background-position: -24px -96px;
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 1px;
}
-.icon-remove-sign {
- background-position: -48px -96px;
+@media (min-width: 768px) {
+ .navbar-right .dropdown-menu {
+ right: 0;
+ left: auto;
+ }
}
-.icon-ok-sign {
- background-position: -72px -96px;
+.btn-default .caret {
+ border-top-color: #333333;
}
-.icon-question-sign {
- background-position: -96px -96px;
+.btn-primary .caret,
+.btn-success .caret,
+.btn-warning .caret,
+.btn-danger .caret,
+.btn-info .caret {
+ border-top-color: #fff;
}
-.icon-info-sign {
- background-position: -120px -96px;
+.dropup .btn-default .caret {
+ border-bottom-color: #333333;
}
-.icon-screenshot {
- background-position: -144px -96px;
+.dropup .btn-primary .caret,
+.dropup .btn-success .caret,
+.dropup .btn-warning .caret,
+.dropup .btn-danger .caret,
+.dropup .btn-info .caret {
+ border-bottom-color: #fff;
}
-.icon-remove-circle {
- background-position: -168px -96px;
+.btn-group,
+.btn-group-vertical {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
}
-.icon-ok-circle {
- background-position: -192px -96px;
+.btn-group > .btn,
+.btn-group-vertical > .btn {
+ position: relative;
+ float: left;
}
-.icon-ban-circle {
- background-position: -216px -96px;
+.btn-group > .btn:hover,
+.btn-group-vertical > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus,
+.btn-group > .btn:active,
+.btn-group-vertical > .btn:active,
+.btn-group > .btn.active,
+.btn-group-vertical > .btn.active {
+ z-index: 2;
}
-.icon-arrow-left {
- background-position: -240px -96px;
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus {
+ outline: none;
}
-.icon-arrow-right {
- background-position: -264px -96px;
+.btn-group .btn + .btn,
+.btn-group .btn + .btn-group,
+.btn-group .btn-group + .btn,
+.btn-group .btn-group + .btn-group {
+ margin-left: -1px;
}
-.icon-arrow-up {
- background-position: -289px -96px;
+.btn-toolbar:before,
+.btn-toolbar:after {
+ display: table;
+ content: " ";
}
-.icon-arrow-down {
- background-position: -312px -96px;
+.btn-toolbar:after {
+ clear: both;
}
-.icon-share-alt {
- background-position: -336px -96px;
+.btn-toolbar:before,
+.btn-toolbar:after {
+ display: table;
+ content: " ";
}
-.icon-resize-full {
- background-position: -360px -96px;
+.btn-toolbar:after {
+ clear: both;
}
-.icon-resize-small {
- background-position: -384px -96px;
+.btn-toolbar .btn-group {
+ float: left;
}
-.icon-plus {
- background-position: -408px -96px;
+.btn-toolbar > .btn + .btn,
+.btn-toolbar > .btn-group + .btn,
+.btn-toolbar > .btn + .btn-group,
+.btn-toolbar > .btn-group + .btn-group {
+ margin-left: 5px;
}
-.icon-minus {
- background-position: -433px -96px;
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+ border-radius: 0;
}
-.icon-asterisk {
- background-position: -456px -96px;
+.btn-group > .btn:first-child {
+ margin-left: 0;
}
-.icon-exclamation-sign {
- background-position: 0 -120px;
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
}
-.icon-gift {
- background-position: -24px -120px;
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
}
-.icon-leaf {
- background-position: -48px -120px;
+.btn-group > .btn-group {
+ float: left;
}
-.icon-fire {
- background-position: -72px -120px;
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-radius: 0;
}
-.icon-eye-open {
- background-position: -96px -120px;
+.btn-group > .btn-group:first-child > .btn:last-child,
+.btn-group > .btn-group:first-child > .dropdown-toggle {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
}
-.icon-eye-close {
- background-position: -120px -120px;
+.btn-group > .btn-group:last-child > .btn:first-child {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
}
-.icon-warning-sign {
- background-position: -144px -120px;
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
}
-.icon-plane {
- background-position: -168px -120px;
+.btn-group-xs > .btn {
+ padding: 5px 10px;
+ padding: 1px 5px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
}
-.icon-calendar {
- background-position: -192px -120px;
+.btn-group-sm > .btn {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
}
-.icon-random {
- width: 16px;
- background-position: -216px -120px;
+.btn-group-lg > .btn {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
}
-.icon-comment {
- background-position: -240px -120px;
+.btn-group > .btn + .dropdown-toggle {
+ padding-right: 8px;
+ padding-left: 8px;
}
-.icon-magnet {
- background-position: -264px -120px;
+.btn-group > .btn-lg + .dropdown-toggle {
+ padding-right: 12px;
+ padding-left: 12px;
}
-.icon-chevron-up {
- background-position: -288px -120px;
+.btn-group.open .dropdown-toggle {
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
-.icon-chevron-down {
- background-position: -313px -119px;
+.btn .caret {
+ margin-left: 0;
}
-.icon-retweet {
- background-position: -336px -120px;
+.btn-lg .caret {
+ border-width: 5px 5px 0;
+ border-bottom-width: 0;
}
-.icon-shopping-cart {
- background-position: -360px -120px;
+.dropup .btn-lg .caret {
+ border-width: 0 5px 5px;
}
-.icon-folder-close {
- width: 16px;
- background-position: -384px -120px;
+.btn-group-vertical > .btn,
+.btn-group-vertical > .btn-group {
+ display: block;
+ float: none;
+ width: 100%;
+ max-width: 100%;
}
-.icon-folder-open {
- width: 16px;
- background-position: -408px -120px;
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after {
+ display: table;
+ content: " ";
}
-.icon-resize-vertical {
- background-position: -432px -119px;
+.btn-group-vertical > .btn-group:after {
+ clear: both;
}
-.icon-resize-horizontal {
- background-position: -456px -118px;
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after {
+ display: table;
+ content: " ";
}
-.icon-hdd {
- background-position: 0 -144px;
+.btn-group-vertical > .btn-group:after {
+ clear: both;
}
-.icon-bullhorn {
- background-position: -24px -144px;
+.btn-group-vertical > .btn-group > .btn {
+ float: none;
}
-.icon-bell {
- background-position: -48px -144px;
+.btn-group-vertical > .btn + .btn,
+.btn-group-vertical > .btn + .btn-group,
+.btn-group-vertical > .btn-group + .btn,
+.btn-group-vertical > .btn-group + .btn-group {
+ margin-top: -1px;
+ margin-left: 0;
}
-.icon-certificate {
- background-position: -72px -144px;
+.btn-group-vertical > .btn:not(:first-child):not(:last-child) {
+ border-radius: 0;
}
-.icon-thumbs-up {
- background-position: -96px -144px;
+.btn-group-vertical > .btn:first-child:not(:last-child) {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
}
-.icon-thumbs-down {
- background-position: -120px -144px;
+.btn-group-vertical > .btn:last-child:not(:first-child) {
+ border-top-right-radius: 0;
+ border-bottom-left-radius: 4px;
+ border-top-left-radius: 0;
}
-.icon-hand-right {
- background-position: -144px -144px;
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-radius: 0;
}
-.icon-hand-left {
- background-position: -168px -144px;
+.btn-group-vertical > .btn-group:first-child > .btn:last-child,
+.btn-group-vertical > .btn-group:first-child > .dropdown-toggle {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
}
-.icon-hand-up {
- background-position: -192px -144px;
+.btn-group-vertical > .btn-group:last-child > .btn:first-child {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
}
-.icon-hand-down {
- background-position: -216px -144px;
+.btn-group-justified {
+ display: table;
+ width: 100%;
+ border-collapse: separate;
+ table-layout: fixed;
}
-.icon-circle-arrow-right {
- background-position: -240px -144px;
+.btn-group-justified .btn {
+ display: table-cell;
+ float: none;
+ width: 1%;
}
-.icon-circle-arrow-left {
- background-position: -264px -144px;
+[data-toggle="buttons"] > .btn > input[type="radio"],
+[data-toggle="buttons"] > .btn > input[type="checkbox"] {
+ display: none;
}
-.icon-circle-arrow-up {
- background-position: -288px -144px;
+.input-group {
+ position: relative;
+ display: table;
+ border-collapse: separate;
}
-.icon-circle-arrow-down {
- background-position: -312px -144px;
+.input-group.col {
+ float: none;
+ padding-right: 0;
+ padding-left: 0;
}
-.icon-globe {
- background-position: -336px -144px;
+.input-group .form-control {
+ width: 100%;
+ margin-bottom: 0;
}
-.icon-wrench {
- background-position: -360px -144px;
+.input-group-lg > .form-control,
+.input-group-lg > .input-group-addon,
+.input-group-lg > .input-group-btn > .btn {
+ height: 45px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
}
-.icon-tasks {
- background-position: -384px -144px;
+select.input-group-lg > .form-control,
+select.input-group-lg > .input-group-addon,
+select.input-group-lg > .input-group-btn > .btn {
+ height: 45px;
+ line-height: 45px;
}
-.icon-filter {
- background-position: -408px -144px;
+textarea.input-group-lg > .form-control,
+textarea.input-group-lg > .input-group-addon,
+textarea.input-group-lg > .input-group-btn > .btn {
+ height: auto;
}
-.icon-briefcase {
- background-position: -432px -144px;
+.input-group-sm > .form-control,
+.input-group-sm > .input-group-addon,
+.input-group-sm > .input-group-btn > .btn {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
}
-.icon-fullscreen {
- background-position: -456px -144px;
+select.input-group-sm > .form-control,
+select.input-group-sm > .input-group-addon,
+select.input-group-sm > .input-group-btn > .btn {
+ height: 30px;
+ line-height: 30px;
}
-.dropup,
-.dropdown {
- position: relative;
+textarea.input-group-sm > .form-control,
+textarea.input-group-sm > .input-group-addon,
+textarea.input-group-sm > .input-group-btn > .btn {
+ height: auto;
}
-.dropdown-toggle {
- *margin-bottom: -3px;
+.input-group-addon,
+.input-group-btn,
+.input-group .form-control {
+ display: table-cell;
}
-.dropdown-toggle:active,
-.open .dropdown-toggle {
- outline: 0;
+.input-group-addon:not(:first-child):not(:last-child),
+.input-group-btn:not(:first-child):not(:last-child),
+.input-group .form-control:not(:first-child):not(:last-child) {
+ border-radius: 0;
}
-.caret {
- display: inline-block;
- width: 0;
- height: 0;
- vertical-align: top;
- border-top: 4px solid #000000;
- border-right: 4px solid transparent;
- border-left: 4px solid transparent;
- content: "";
+.input-group-addon,
+.input-group-btn {
+ width: 1%;
+ white-space: nowrap;
+ vertical-align: middle;
}
-.dropdown .caret {
- margin-top: 8px;
- margin-left: 2px;
+.input-group-addon {
+ padding: 6px 12px;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 1;
+ text-align: center;
+ background-color: #eeeeee;
+ border: 1px solid #cccccc;
+ border-radius: 4px;
}
-.dropdown-menu {
- position: absolute;
- top: 100%;
- left: 0;
- z-index: 1000;
- display: none;
- float: left;
- min-width: 160px;
- padding: 5px 0;
- margin: 2px 0 0;
- list-style: none;
- background-color: #ffffff;
- border: 1px solid #ccc;
- border: 1px solid rgba(0, 0, 0, 0.2);
- *border-right-width: 2px;
- *border-bottom-width: 2px;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
- -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding;
- background-clip: padding-box;
+.input-group-addon.input-sm {
+ padding: 5px 10px;
+ font-size: 12px;
+ border-radius: 3px;
}
-.dropdown-menu.pull-right {
- right: 0;
- left: auto;
+.input-group-addon.input-lg {
+ padding: 10px 16px;
+ font-size: 18px;
+ border-radius: 6px;
}
-.dropdown-menu .divider {
- *width: 100%;
- height: 1px;
- margin: 9px 1px;
- *margin: -5px 0 5px;
- overflow: hidden;
- background-color: #e5e5e5;
- border-bottom: 1px solid #ffffff;
+.input-group-addon input[type="radio"],
+.input-group-addon input[type="checkbox"] {
+ margin-top: 0;
}
-.dropdown-menu > li > a {
- display: block;
- padding: 3px 20px;
- clear: both;
- font-weight: normal;
- line-height: 20px;
- color: #333333;
- white-space: nowrap;
+.input-group .form-control:first-child,
+.input-group-addon:first-child,
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .dropdown-toggle,
+.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
}
-.dropdown-menu > li > a:hover,
-.dropdown-menu > li > a:focus,
-.dropdown-submenu:hover > a,
-.dropdown-submenu:focus > a {
- color: #ffffff;
- text-decoration: none;
- background-color: #0081c2;
- background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
- background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
- background-image: -o-linear-gradient(top, #0088cc, #0077b3);
- background-image: linear-gradient(to bottom, #0088cc, #0077b3);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+.input-group-addon:first-child {
+ border-right: 0;
}
-.dropdown-menu > .active > a,
-.dropdown-menu > .active > a:hover,
-.dropdown-menu > .active > a:focus {
- color: #ffffff;
- text-decoration: none;
- background-color: #0081c2;
- background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
- background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
- background-image: -o-linear-gradient(top, #0088cc, #0077b3);
- background-image: linear-gradient(to bottom, #0088cc, #0077b3);
- background-repeat: repeat-x;
- outline: 0;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+.input-group .form-control:last-child,
+.input-group-addon:last-child,
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .dropdown-toggle,
+.input-group-btn:first-child > .btn:not(:first-child) {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
}
-.dropdown-menu > .disabled > a,
-.dropdown-menu > .disabled > a:hover,
-.dropdown-menu > .disabled > a:focus {
- color: #999999;
+.input-group-addon:last-child {
+ border-left: 0;
}
-.dropdown-menu > .disabled > a:hover,
-.dropdown-menu > .disabled > a:focus {
- text-decoration: none;
- cursor: default;
- background-color: transparent;
- background-image: none;
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+.input-group-btn {
+ position: relative;
+ white-space: nowrap;
}
-.open {
- *z-index: 1000;
+.input-group-btn > .btn {
+ position: relative;
}
-.open > .dropdown-menu {
- display: block;
+.input-group-btn > .btn + .btn {
+ margin-left: -4px;
}
-.dropdown-backdrop {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 990;
+.input-group-btn > .btn:hover,
+.input-group-btn > .btn:active {
+ z-index: 2;
}
-.pull-right > .dropdown-menu {
- right: 0;
- left: auto;
+.nav {
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
}
-.dropup .caret,
-.navbar-fixed-bottom .dropdown .caret {
- border-top: 0;
- border-bottom: 4px solid #000000;
- content: "";
+.nav:before,
+.nav:after {
+ display: table;
+ content: " ";
}
-.dropup .dropdown-menu,
-.navbar-fixed-bottom .dropdown .dropdown-menu {
- top: auto;
- bottom: 100%;
- margin-bottom: 1px;
+.nav:after {
+ clear: both;
}
-.dropdown-submenu {
- position: relative;
+.nav:before,
+.nav:after {
+ display: table;
+ content: " ";
}
-.dropdown-submenu > .dropdown-menu {
- top: 0;
- left: 100%;
- margin-top: -6px;
- margin-left: -1px;
- -webkit-border-radius: 0 6px 6px 6px;
- -moz-border-radius: 0 6px 6px 6px;
- border-radius: 0 6px 6px 6px;
+.nav:after {
+ clear: both;
}
-.dropdown-submenu:hover > .dropdown-menu {
+.nav > li {
+ position: relative;
display: block;
}
-.dropup .dropdown-submenu > .dropdown-menu {
- top: auto;
- bottom: 0;
- margin-top: 0;
- margin-bottom: -2px;
- -webkit-border-radius: 5px 5px 5px 0;
- -moz-border-radius: 5px 5px 5px 0;
- border-radius: 5px 5px 5px 0;
+.nav > li > a {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
}
-.dropdown-submenu > a:after {
- display: block;
- float: right;
- width: 0;
- height: 0;
- margin-top: 5px;
- margin-right: -10px;
- border-color: transparent;
- border-left-color: #cccccc;
- border-style: solid;
- border-width: 5px 0 5px 5px;
- content: " ";
+.nav > li > a:hover,
+.nav > li > a:focus {
+ text-decoration: none;
+ background-color: #eeeeee;
}
-.dropdown-submenu:hover > a:after {
- border-left-color: #ffffff;
+.nav > li.disabled > a {
+ color: #999999;
}
-.dropdown-submenu.pull-left {
- float: none;
+.nav > li.disabled > a:hover,
+.nav > li.disabled > a:focus {
+ color: #999999;
+ text-decoration: none;
+ cursor: not-allowed;
+ background-color: transparent;
}
-.dropdown-submenu.pull-left > .dropdown-menu {
- left: -100%;
- margin-left: 10px;
- -webkit-border-radius: 6px 0 6px 6px;
- -moz-border-radius: 6px 0 6px 6px;
- border-radius: 6px 0 6px 6px;
+.nav .open > a,
+.nav .open > a:hover,
+.nav .open > a:focus {
+ background-color: #eeeeee;
+ border-color: #428bca;
}
-.dropdown .dropdown-menu .nav-header {
- padding-right: 20px;
- padding-left: 20px;
+.nav .nav-divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5;
}
-.typeahead {
- z-index: 1051;
- margin-top: 2px;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
+.nav > li > a > img {
+ max-width: none;
}
-.well {
- min-height: 20px;
- padding: 19px;
- margin-bottom: 20px;
- background-color: #f5f5f5;
- border: 1px solid #e3e3e3;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+.nav-tabs {
+ border-bottom: 1px solid #dddddd;
}
-.well blockquote {
- border-color: #ddd;
- border-color: rgba(0, 0, 0, 0.15);
+.nav-tabs > li {
+ float: left;
+ margin-bottom: -1px;
}
-.well-large {
- padding: 24px;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
+.nav-tabs > li > a {
+ margin-right: 2px;
+ line-height: 1.428571429;
+ border: 1px solid transparent;
+ border-radius: 4px 4px 0 0;
}
-.well-small {
- padding: 9px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
+.nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #dddddd;
}
-.fade {
- opacity: 0;
- -webkit-transition: opacity 0.15s linear;
- -moz-transition: opacity 0.15s linear;
- -o-transition: opacity 0.15s linear;
- transition: opacity 0.15s linear;
+.nav-tabs > li.active > a,
+.nav-tabs > li.active > a:hover,
+.nav-tabs > li.active > a:focus {
+ color: #555555;
+ cursor: default;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-bottom-color: transparent;
}
-.fade.in {
- opacity: 1;
+.nav-tabs.nav-justified {
+ width: 100%;
+ border-bottom: 0;
}
-.collapse {
- position: relative;
- height: 0;
- overflow: hidden;
- -webkit-transition: height 0.35s ease;
- -moz-transition: height 0.35s ease;
- -o-transition: height 0.35s ease;
- transition: height 0.35s ease;
+.nav-tabs.nav-justified > li {
+ float: none;
}
-.collapse.in {
- height: auto;
+.nav-tabs.nav-justified > li > a {
+ text-align: center;
}
-.close {
- float: right;
- font-size: 20px;
- font-weight: bold;
- line-height: 20px;
- color: #000000;
- text-shadow: 0 1px 0 #ffffff;
- opacity: 0.2;
- filter: alpha(opacity=20);
+@media (min-width: 768px) {
+ .nav-tabs.nav-justified > li {
+ display: table-cell;
+ width: 1%;
+ }
}
-.close:hover,
-.close:focus {
- color: #000000;
- text-decoration: none;
- cursor: pointer;
- opacity: 0.4;
- filter: alpha(opacity=40);
+.nav-tabs.nav-justified > li > a {
+ margin-right: 0;
+ border-bottom: 1px solid #dddddd;
}
-button.close {
- padding: 0;
- cursor: pointer;
- background: transparent;
- border: 0;
- -webkit-appearance: none;
+.nav-tabs.nav-justified > .active > a {
+ border-bottom-color: #ffffff;
}
-.btn {
- display: inline-block;
- *display: inline;
- padding: 4px 12px;
- margin-bottom: 0;
- *margin-left: .3em;
- font-size: 14px;
- line-height: 20px;
- color: #333333;
- text-align: center;
- text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
- vertical-align: middle;
- cursor: pointer;
- background-color: #f5f5f5;
- *background-color: #e6e6e6;
- background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
- background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
- background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
- background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
- background-repeat: repeat-x;
- border: 1px solid #cccccc;
- *border: 0;
- border-color: #e6e6e6 #e6e6e6 #bfbfbf;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- border-bottom-color: #b3b3b3;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
- *zoom: 1;
- -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+.nav-pills > li {
+ float: left;
}
-.btn:hover,
-.btn:focus,
-.btn:active,
-.btn.active,
-.btn.disabled,
-.btn[disabled] {
- color: #333333;
- background-color: #e6e6e6;
- *background-color: #d9d9d9;
+.nav-pills > li > a {
+ border-radius: 5px;
}
-.btn:active,
-.btn.active {
- background-color: #cccccc \9;
+.nav-pills > li + li {
+ margin-left: 2px;
}
-.btn:first-child {
- *margin-left: 0;
+.nav-pills > li.active > a,
+.nav-pills > li.active > a:hover,
+.nav-pills > li.active > a:focus {
+ color: #ffffff;
+ background-color: #428bca;
}
-.btn:hover,
-.btn:focus {
- color: #333333;
- text-decoration: none;
- background-position: 0 -15px;
- -webkit-transition: background-position 0.1s linear;
- -moz-transition: background-position 0.1s linear;
- -o-transition: background-position 0.1s linear;
- transition: background-position 0.1s linear;
+.nav-stacked > li {
+ float: none;
}
-.btn:focus {
- outline: thin dotted #333;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
+.nav-stacked > li + li {
+ margin-top: 2px;
+ margin-left: 0;
}
-.btn.active,
-.btn:active {
- background-image: none;
- outline: 0;
- -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
- box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+.nav-justified {
+ width: 100%;
}
-.btn.disabled,
-.btn[disabled] {
- cursor: default;
- background-image: none;
- opacity: 0.65;
- filter: alpha(opacity=65);
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
+.nav-justified > li {
+ float: none;
}
-.btn-large {
- padding: 11px 19px;
- font-size: 17.5px;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
+.nav-justified > li > a {
+ text-align: center;
}
-.btn-large [class^="icon-"],
-.btn-large [class*=" icon-"] {
- margin-top: 4px;
+@media (min-width: 768px) {
+ .nav-justified > li {
+ display: table-cell;
+ width: 1%;
+ }
}
-.btn-small {
- padding: 2px 10px;
- font-size: 11.9px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
+.nav-tabs-justified {
+ border-bottom: 0;
}
-.btn-small [class^="icon-"],
-.btn-small [class*=" icon-"] {
- margin-top: 0;
+.nav-tabs-justified > li > a {
+ margin-right: 0;
+ border-bottom: 1px solid #dddddd;
}
-.btn-mini [class^="icon-"],
-.btn-mini [class*=" icon-"] {
- margin-top: -1px;
+.nav-tabs-justified > .active > a {
+ border-bottom-color: #ffffff;
}
-.btn-mini {
- padding: 0 6px;
- font-size: 10.5px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
+.tabbable:before,
+.tabbable:after {
+ display: table;
+ content: " ";
}
-.btn-block {
- display: block;
- width: 100%;
- padding-right: 0;
- padding-left: 0;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
+.tabbable:after {
+ clear: both;
}
-.btn-block + .btn-block {
- margin-top: 5px;
+.tabbable:before,
+.tabbable:after {
+ display: table;
+ content: " ";
}
-input[type="submit"].btn-block,
-input[type="reset"].btn-block,
-input[type="button"].btn-block {
- width: 100%;
+.tabbable:after {
+ clear: both;
}
-.btn-primary.active,
-.btn-warning.active,
-.btn-danger.active,
-.btn-success.active,
-.btn-info.active,
-.btn-inverse.active {
- color: rgba(255, 255, 255, 0.75);
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+ display: none;
}
-.btn-primary {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #006dcc;
- *background-color: #0044cc;
- background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
- background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
- background-image: -o-linear-gradient(top, #0088cc, #0044cc);
- background-image: linear-gradient(to bottom, #0088cc, #0044cc);
- background-repeat: repeat-x;
- border-color: #0044cc #0044cc #002a80;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+.tab-content > .active,
+.pill-content > .active {
+ display: block;
}
-.btn-primary:hover,
-.btn-primary:focus,
-.btn-primary:active,
-.btn-primary.active,
-.btn-primary.disabled,
-.btn-primary[disabled] {
- color: #ffffff;
- background-color: #0044cc;
- *background-color: #003bb3;
+.nav .caret {
+ border-top-color: #428bca;
+ border-bottom-color: #428bca;
+}
+
+.nav a:hover .caret {
+ border-top-color: #2a6496;
+ border-bottom-color: #2a6496;
}
-.btn-primary:active,
-.btn-primary.active {
- background-color: #003399 \9;
+.nav-tabs .dropdown-menu {
+ margin-top: -1px;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
}
-.btn-warning {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #faa732;
- *background-color: #f89406;
- background-image: -moz-linear-gradient(top, #fbb450, #f89406);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
- background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
- background-image: -o-linear-gradient(top, #fbb450, #f89406);
- background-image: linear-gradient(to bottom, #fbb450, #f89406);
- background-repeat: repeat-x;
- border-color: #f89406 #f89406 #ad6704;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+.navbar {
+ position: relative;
+ z-index: 1000;
+ min-height: 50px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
}
-.btn-warning:hover,
-.btn-warning:focus,
-.btn-warning:active,
-.btn-warning.active,
-.btn-warning.disabled,
-.btn-warning[disabled] {
- color: #ffffff;
- background-color: #f89406;
- *background-color: #df8505;
+.navbar:before,
+.navbar:after {
+ display: table;
+ content: " ";
}
-.btn-warning:active,
-.btn-warning.active {
- background-color: #c67605 \9;
+.navbar:after {
+ clear: both;
}
-.btn-danger {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #da4f49;
- *background-color: #bd362f;
- background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
- background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
- background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
- background-image: linear-gradient(to bottom, #ee5f5b, #bd362f);
- background-repeat: repeat-x;
- border-color: #bd362f #bd362f #802420;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+.navbar:before,
+.navbar:after {
+ display: table;
+ content: " ";
}
-.btn-danger:hover,
-.btn-danger:focus,
-.btn-danger:active,
-.btn-danger.active,
-.btn-danger.disabled,
-.btn-danger[disabled] {
- color: #ffffff;
- background-color: #bd362f;
- *background-color: #a9302a;
+.navbar:after {
+ clear: both;
}
-.btn-danger:active,
-.btn-danger.active {
- background-color: #942a25 \9;
+@media (min-width: 768px) {
+ .navbar {
+ border-radius: 4px;
+ }
}
-.btn-success {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #5bb75b;
- *background-color: #51a351;
- background-image: -moz-linear-gradient(top, #62c462, #51a351);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
- background-image: -webkit-linear-gradient(top, #62c462, #51a351);
- background-image: -o-linear-gradient(top, #62c462, #51a351);
- background-image: linear-gradient(to bottom, #62c462, #51a351);
- background-repeat: repeat-x;
- border-color: #51a351 #51a351 #387038;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+.navbar-header:before,
+.navbar-header:after {
+ display: table;
+ content: " ";
}
-.btn-success:hover,
-.btn-success:focus,
-.btn-success:active,
-.btn-success.active,
-.btn-success.disabled,
-.btn-success[disabled] {
- color: #ffffff;
- background-color: #51a351;
- *background-color: #499249;
+.navbar-header:after {
+ clear: both;
}
-.btn-success:active,
-.btn-success.active {
- background-color: #408140 \9;
+.navbar-header:before,
+.navbar-header:after {
+ display: table;
+ content: " ";
}
-.btn-info {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #49afcd;
- *background-color: #2f96b4;
- background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));
- background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
- background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
- background-image: linear-gradient(to bottom, #5bc0de, #2f96b4);
- background-repeat: repeat-x;
- border-color: #2f96b4 #2f96b4 #1f6377;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+.navbar-header:after {
+ clear: both;
}
-.btn-info:hover,
-.btn-info:focus,
-.btn-info:active,
-.btn-info.active,
-.btn-info.disabled,
-.btn-info[disabled] {
- color: #ffffff;
- background-color: #2f96b4;
- *background-color: #2a85a0;
+@media (min-width: 768px) {
+ .navbar-header {
+ float: left;
+ }
}
-.btn-info:active,
-.btn-info.active {
- background-color: #24748c \9;
+.navbar-collapse {
+ max-height: 340px;
+ padding-right: 15px;
+ padding-left: 15px;
+ overflow-x: visible;
+ border-top: 1px solid transparent;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ -webkit-overflow-scrolling: touch;
}
-.btn-inverse {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #363636;
- *background-color: #222222;
- background-image: -moz-linear-gradient(top, #444444, #222222);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));
- background-image: -webkit-linear-gradient(top, #444444, #222222);
- background-image: -o-linear-gradient(top, #444444, #222222);
- background-image: linear-gradient(to bottom, #444444, #222222);
- background-repeat: repeat-x;
- border-color: #222222 #222222 #000000;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+.navbar-collapse:before,
+.navbar-collapse:after {
+ display: table;
+ content: " ";
}
-.btn-inverse:hover,
-.btn-inverse:focus,
-.btn-inverse:active,
-.btn-inverse.active,
-.btn-inverse.disabled,
-.btn-inverse[disabled] {
- color: #ffffff;
- background-color: #222222;
- *background-color: #151515;
+.navbar-collapse:after {
+ clear: both;
}
-.btn-inverse:active,
-.btn-inverse.active {
- background-color: #080808 \9;
+.navbar-collapse:before,
+.navbar-collapse:after {
+ display: table;
+ content: " ";
}
-button.btn,
-input[type="submit"].btn {
- *padding-top: 3px;
- *padding-bottom: 3px;
+.navbar-collapse:after {
+ clear: both;
}
-button.btn::-moz-focus-inner,
-input[type="submit"].btn::-moz-focus-inner {
- padding: 0;
- border: 0;
+.navbar-collapse.in {
+ overflow-y: auto;
}
-button.btn.btn-large,
-input[type="submit"].btn.btn-large {
- *padding-top: 7px;
- *padding-bottom: 7px;
+@media (min-width: 768px) {
+ .navbar-collapse {
+ width: auto;
+ border-top: 0;
+ box-shadow: none;
+ }
+ .navbar-collapse.collapse {
+ display: block !important;
+ height: auto !important;
+ padding-bottom: 0;
+ overflow: visible !important;
+ }
+ .navbar-collapse.in {
+ overflow-y: visible;
+ }
+ .navbar-collapse .navbar-nav.navbar-left:first-child {
+ margin-left: -15px;
+ }
+ .navbar-collapse .navbar-nav.navbar-right:last-child {
+ margin-right: -15px;
+ }
+ .navbar-collapse .navbar-text:last-child {
+ margin-right: 0;
+ }
}
-button.btn.btn-small,
-input[type="submit"].btn.btn-small {
- *padding-top: 3px;
- *padding-bottom: 3px;
+.container > .navbar-header,
+.container > .navbar-collapse {
+ margin-right: -15px;
+ margin-left: -15px;
}
-button.btn.btn-mini,
-input[type="submit"].btn.btn-mini {
- *padding-top: 1px;
- *padding-bottom: 1px;
+@media (min-width: 768px) {
+ .container > .navbar-header,
+ .container > .navbar-collapse {
+ margin-right: 0;
+ margin-left: 0;
+ }
}
-.btn-link,
-.btn-link:active,
-.btn-link[disabled] {
- background-color: transparent;
- background-image: none;
- -webkit-box-shadow: none;
- -moz-box-shadow: none;
- box-shadow: none;
+.navbar-static-top {
+ border-width: 0 0 1px;
}
-.btn-link {
- color: #0088cc;
- cursor: pointer;
- border-color: transparent;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
+@media (min-width: 768px) {
+ .navbar-static-top {
+ border-radius: 0;
+ }
}
-.btn-link:hover,
-.btn-link:focus {
- color: #005580;
- text-decoration: underline;
- background-color: transparent;
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ border-width: 0 0 1px;
}
-.btn-link[disabled]:hover,
-.btn-link[disabled]:focus {
- color: #333333;
- text-decoration: none;
+@media (min-width: 768px) {
+ .navbar-fixed-top,
+ .navbar-fixed-bottom {
+ border-radius: 0;
+ }
}
-.btn-group {
- position: relative;
- display: inline-block;
- *display: inline;
- *margin-left: .3em;
- font-size: 0;
- white-space: nowrap;
- vertical-align: middle;
- *zoom: 1;
+.navbar-fixed-top {
+ top: 0;
+ z-index: 1030;
}
-.btn-group:first-child {
- *margin-left: 0;
+.navbar-fixed-bottom {
+ bottom: 0;
+ margin-bottom: 0;
}
-.btn-group + .btn-group {
- margin-left: 5px;
+.navbar-brand {
+ float: left;
+ padding: 15px 15px;
+ font-size: 18px;
+ line-height: 20px;
}
-.btn-toolbar {
- margin-top: 10px;
- margin-bottom: 10px;
- font-size: 0;
+.navbar-brand:hover,
+.navbar-brand:focus {
+ text-decoration: none;
}
-.btn-toolbar > .btn + .btn,
-.btn-toolbar > .btn-group + .btn,
-.btn-toolbar > .btn + .btn-group {
- margin-left: 5px;
+@media (min-width: 768px) {
+ .navbar > .container .navbar-brand {
+ margin-left: -15px;
+ }
}
-.btn-group > .btn {
+.navbar-toggle {
position: relative;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
+ float: right;
+ padding: 9px 10px;
+ margin-top: 8px;
+ margin-right: 15px;
+ margin-bottom: 8px;
+ background-color: transparent;
+ border: 1px solid transparent;
+ border-radius: 4px;
}
-.btn-group > .btn + .btn {
- margin-left: -1px;
+.navbar-toggle .icon-bar {
+ display: block;
+ width: 22px;
+ height: 2px;
+ border-radius: 1px;
}
-.btn-group > .btn,
-.btn-group > .dropdown-menu,
-.btn-group > .popover {
- font-size: 14px;
+.navbar-toggle .icon-bar + .icon-bar {
+ margin-top: 4px;
}
-.btn-group > .btn-mini {
- font-size: 10.5px;
+@media (min-width: 768px) {
+ .navbar-toggle {
+ display: none;
+ }
}
-.btn-group > .btn-small {
- font-size: 11.9px;
+.navbar-nav {
+ margin: 7.5px -15px;
}
-.btn-group > .btn-large {
- font-size: 17.5px;
+.navbar-nav > li > a {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ line-height: 20px;
}
-.btn-group > .btn:first-child {
- margin-left: 0;
- -webkit-border-bottom-left-radius: 4px;
- border-bottom-left-radius: 4px;
- -webkit-border-top-left-radius: 4px;
- border-top-left-radius: 4px;
- -moz-border-radius-bottomleft: 4px;
- -moz-border-radius-topleft: 4px;
+@media (max-width: 767px) {
+ .navbar-nav .open .dropdown-menu {
+ position: static;
+ float: none;
+ width: auto;
+ margin-top: 0;
+ background-color: transparent;
+ border: 0;
+ box-shadow: none;
+ }
+ .navbar-nav .open .dropdown-menu > li > a,
+ .navbar-nav .open .dropdown-menu .dropdown-header {
+ padding: 5px 15px 5px 25px;
+ }
+ .navbar-nav .open .dropdown-menu > li > a {
+ line-height: 20px;
+ }
+ .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-nav .open .dropdown-menu > li > a:focus {
+ background-image: none;
+ }
}
-.btn-group > .btn:last-child,
-.btn-group > .dropdown-toggle {
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
- -webkit-border-bottom-right-radius: 4px;
- border-bottom-right-radius: 4px;
- -moz-border-radius-topright: 4px;
- -moz-border-radius-bottomright: 4px;
+@media (min-width: 768px) {
+ .navbar-nav {
+ float: left;
+ margin: 0;
+ }
+ .navbar-nav > li {
+ float: left;
+ }
+ .navbar-nav > li > a {
+ padding-top: 15px;
+ padding-bottom: 15px;
+ }
}
-.btn-group > .btn.large:first-child {
- margin-left: 0;
- -webkit-border-bottom-left-radius: 6px;
- border-bottom-left-radius: 6px;
- -webkit-border-top-left-radius: 6px;
- border-top-left-radius: 6px;
- -moz-border-radius-bottomleft: 6px;
- -moz-border-radius-topleft: 6px;
+@media (min-width: 768px) {
+ .navbar-left {
+ float: left !important;
+ }
+ .navbar-right {
+ float: right !important;
+ }
}
-.btn-group > .btn.large:last-child,
-.btn-group > .large.dropdown-toggle {
- -webkit-border-top-right-radius: 6px;
- border-top-right-radius: 6px;
- -webkit-border-bottom-right-radius: 6px;
- border-bottom-right-radius: 6px;
- -moz-border-radius-topright: 6px;
- -moz-border-radius-bottomright: 6px;
+.navbar-form {
+ padding: 10px 15px;
+ margin-top: 8px;
+ margin-right: -15px;
+ margin-bottom: 8px;
+ margin-left: -15px;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
}
-.btn-group > .btn:hover,
-.btn-group > .btn:focus,
-.btn-group > .btn:active,
-.btn-group > .btn.active {
- z-index: 2;
+@media (min-width: 768px) {
+ .navbar-form .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .navbar-form .form-control {
+ display: inline-block;
+ }
+ .navbar-form .radio,
+ .navbar-form .checkbox {
+ display: inline-block;
+ padding-left: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+ .navbar-form .radio input[type="radio"],
+ .navbar-form .checkbox input[type="checkbox"] {
+ float: none;
+ margin-left: 0;
+ }
}
-.btn-group .dropdown-toggle:active,
-.btn-group.open .dropdown-toggle {
- outline: 0;
+@media (max-width: 767px) {
+ .navbar-form .form-group {
+ margin-bottom: 5px;
+ }
}
-.btn-group > .btn + .dropdown-toggle {
- *padding-top: 5px;
- padding-right: 8px;
- *padding-bottom: 5px;
- padding-left: 8px;
- -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
- box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+@media (min-width: 768px) {
+ .navbar-form {
+ width: auto;
+ padding-top: 0;
+ padding-bottom: 0;
+ margin-right: 0;
+ margin-left: 0;
+ border: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ }
}
-.btn-group > .btn-mini + .dropdown-toggle {
- *padding-top: 2px;
- padding-right: 5px;
- *padding-bottom: 2px;
- padding-left: 5px;
+.navbar-nav > li > .dropdown-menu {
+ margin-top: 0;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
}
-.btn-group > .btn-small + .dropdown-toggle {
- *padding-top: 5px;
- *padding-bottom: 4px;
+.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
}
-.btn-group > .btn-large + .dropdown-toggle {
- *padding-top: 7px;
- padding-right: 12px;
- *padding-bottom: 7px;
- padding-left: 12px;
+.navbar-nav.pull-right > li > .dropdown-menu,
+.navbar-nav > li > .dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
}
-.btn-group.open .dropdown-toggle {
- background-image: none;
- -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
- box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+.navbar-btn {
+ margin-top: 8px;
+ margin-bottom: 8px;
}
-.btn-group.open .btn.dropdown-toggle {
- background-color: #e6e6e6;
+.navbar-text {
+ float: left;
+ margin-top: 15px;
+ margin-bottom: 15px;
}
-.btn-group.open .btn-primary.dropdown-toggle {
- background-color: #0044cc;
+@media (min-width: 768px) {
+ .navbar-text {
+ margin-right: 15px;
+ margin-left: 15px;
+ }
}
-.btn-group.open .btn-warning.dropdown-toggle {
- background-color: #f89406;
+.navbar-default {
+ background-color: #f8f8f8;
+ border-color: #e7e7e7;
}
-.btn-group.open .btn-danger.dropdown-toggle {
- background-color: #bd362f;
+.navbar-default .navbar-brand {
+ color: #777777;
}
-.btn-group.open .btn-success.dropdown-toggle {
- background-color: #51a351;
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus {
+ color: #5e5e5e;
+ background-color: transparent;
}
-.btn-group.open .btn-info.dropdown-toggle {
- background-color: #2f96b4;
+.navbar-default .navbar-text {
+ color: #777777;
}
-.btn-group.open .btn-inverse.dropdown-toggle {
- background-color: #222222;
+.navbar-default .navbar-nav > li > a {
+ color: #777777;
}
-.btn .caret {
- margin-top: 8px;
- margin-left: 0;
+.navbar-default .navbar-nav > li > a:hover,
+.navbar-default .navbar-nav > li > a:focus {
+ color: #333333;
+ background-color: transparent;
}
-.btn-large .caret {
- margin-top: 6px;
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover,
+.navbar-default .navbar-nav > .active > a:focus {
+ color: #555555;
+ background-color: #e7e7e7;
}
-.btn-large .caret {
- border-top-width: 5px;
- border-right-width: 5px;
- border-left-width: 5px;
+.navbar-default .navbar-nav > .disabled > a,
+.navbar-default .navbar-nav > .disabled > a:hover,
+.navbar-default .navbar-nav > .disabled > a:focus {
+ color: #cccccc;
+ background-color: transparent;
}
-.btn-mini .caret,
-.btn-small .caret {
- margin-top: 8px;
+.navbar-default .navbar-toggle {
+ border-color: #dddddd;
}
-.dropup .btn-large .caret {
- border-bottom-width: 5px;
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+ background-color: #dddddd;
}
-.btn-primary .caret,
-.btn-warning .caret,
-.btn-danger .caret,
-.btn-info .caret,
-.btn-success .caret,
-.btn-inverse .caret {
- border-top-color: #ffffff;
- border-bottom-color: #ffffff;
+.navbar-default .navbar-toggle .icon-bar {
+ background-color: #cccccc;
}
-.btn-group-vertical {
- display: inline-block;
- *display: inline;
- /* IE7 inline-block hack */
-
- *zoom: 1;
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+ border-color: #e6e6e6;
}
-.btn-group-vertical > .btn {
- display: block;
- float: none;
- max-width: 100%;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
+.navbar-default .navbar-nav > .dropdown > a:hover .caret,
+.navbar-default .navbar-nav > .dropdown > a:focus .caret {
+ border-top-color: #333333;
+ border-bottom-color: #333333;
}
-.btn-group-vertical > .btn + .btn {
- margin-top: -1px;
- margin-left: 0;
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .open > a:hover,
+.navbar-default .navbar-nav > .open > a:focus {
+ color: #555555;
+ background-color: #e7e7e7;
}
-.btn-group-vertical > .btn:first-child {
- -webkit-border-radius: 4px 4px 0 0;
- -moz-border-radius: 4px 4px 0 0;
- border-radius: 4px 4px 0 0;
+.navbar-default .navbar-nav > .open > a .caret,
+.navbar-default .navbar-nav > .open > a:hover .caret,
+.navbar-default .navbar-nav > .open > a:focus .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
}
-.btn-group-vertical > .btn:last-child {
- -webkit-border-radius: 0 0 4px 4px;
- -moz-border-radius: 0 0 4px 4px;
- border-radius: 0 0 4px 4px;
+.navbar-default .navbar-nav > .dropdown > a .caret {
+ border-top-color: #777777;
+ border-bottom-color: #777777;
}
-.btn-group-vertical > .btn-large:first-child {
- -webkit-border-radius: 6px 6px 0 0;
- -moz-border-radius: 6px 6px 0 0;
- border-radius: 6px 6px 0 0;
+@media (max-width: 767px) {
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a {
+ color: #777777;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
+ color: #333333;
+ background-color: transparent;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #555555;
+ background-color: #e7e7e7;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ color: #cccccc;
+ background-color: transparent;
+ }
}
-.btn-group-vertical > .btn-large:last-child {
- -webkit-border-radius: 0 0 6px 6px;
- -moz-border-radius: 0 0 6px 6px;
- border-radius: 0 0 6px 6px;
+.navbar-default .navbar-link {
+ color: #777777;
}
-.alert {
- padding: 8px 35px 8px 14px;
- margin-bottom: 20px;
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
- background-color: #fcf8e3;
- border: 1px solid #fbeed5;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
+.navbar-default .navbar-link:hover {
+ color: #333333;
}
-.alert,
-.alert h4 {
- color: #c09853;
+.navbar-inverse {
+ background-color: #222222;
+ border-color: #080808;
}
-.alert h4 {
- margin: 0;
+.navbar-inverse .navbar-brand {
+ color: #999999;
}
-.alert .close {
- position: relative;
- top: -2px;
- right: -21px;
- line-height: 20px;
+.navbar-inverse .navbar-brand:hover,
+.navbar-inverse .navbar-brand:focus {
+ color: #ffffff;
+ background-color: transparent;
}
-.alert-success {
- color: #468847;
- background-color: #dff0d8;
- border-color: #d6e9c6;
+.navbar-inverse .navbar-text {
+ color: #999999;
}
-.alert-success h4 {
- color: #468847;
+.navbar-inverse .navbar-nav > li > a {
+ color: #999999;
}
-.alert-danger,
-.alert-error {
- color: #b94a48;
- background-color: #f2dede;
- border-color: #eed3d7;
+.navbar-inverse .navbar-nav > li > a:hover,
+.navbar-inverse .navbar-nav > li > a:focus {
+ color: #ffffff;
+ background-color: transparent;
}
-.alert-danger h4,
-.alert-error h4 {
- color: #b94a48;
+.navbar-inverse .navbar-nav > .active > a,
+.navbar-inverse .navbar-nav > .active > a:hover,
+.navbar-inverse .navbar-nav > .active > a:focus {
+ color: #ffffff;
+ background-color: #080808;
}
-.alert-info {
- color: #3a87ad;
- background-color: #d9edf7;
- border-color: #bce8f1;
+.navbar-inverse .navbar-nav > .disabled > a,
+.navbar-inverse .navbar-nav > .disabled > a:hover,
+.navbar-inverse .navbar-nav > .disabled > a:focus {
+ color: #444444;
+ background-color: transparent;
}
-.alert-info h4 {
- color: #3a87ad;
+.navbar-inverse .navbar-toggle {
+ border-color: #333333;
}
-.alert-block {
- padding-top: 14px;
- padding-bottom: 14px;
+.navbar-inverse .navbar-toggle:hover,
+.navbar-inverse .navbar-toggle:focus {
+ background-color: #333333;
}
-.alert-block > p,
-.alert-block > ul {
- margin-bottom: 0;
+.navbar-inverse .navbar-toggle .icon-bar {
+ background-color: #ffffff;
}
-.alert-block p + p {
- margin-top: 5px;
+.navbar-inverse .navbar-collapse,
+.navbar-inverse .navbar-form {
+ border-color: #101010;
}
-.nav {
- margin-bottom: 20px;
- margin-left: 0;
- list-style: none;
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .open > a:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+ color: #ffffff;
+ background-color: #080808;
}
-.nav > li > a {
- display: block;
+.navbar-inverse .navbar-nav > .dropdown > a:hover .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
}
-.nav > li > a:hover,
-.nav > li > a:focus {
- text-decoration: none;
- background-color: #eeeeee;
+.navbar-inverse .navbar-nav > .dropdown > a .caret {
+ border-top-color: #999999;
+ border-bottom-color: #999999;
}
-.nav > li > a > img {
- max-width: none;
+.navbar-inverse .navbar-nav > .open > a .caret,
+.navbar-inverse .navbar-nav > .open > a:hover .caret,
+.navbar-inverse .navbar-nav > .open > a:focus .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
}
-.nav > .pull-right {
- float: right;
+@media (max-width: 767px) {
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
+ border-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
+ color: #999999;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
+ color: #ffffff;
+ background-color: transparent;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #ffffff;
+ background-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ color: #444444;
+ background-color: transparent;
+ }
}
-.nav-header {
- display: block;
- padding: 3px 15px;
- font-size: 11px;
- font-weight: bold;
- line-height: 20px;
+.navbar-inverse .navbar-link {
color: #999999;
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
- text-transform: uppercase;
}
-.nav li + .nav-header {
- margin-top: 9px;
+.navbar-inverse .navbar-link:hover {
+ color: #ffffff;
}
-.nav-list {
- padding-right: 15px;
- padding-left: 15px;
- margin-bottom: 0;
+.breadcrumb {
+ padding: 8px 15px;
+ margin-bottom: 20px;
+ list-style: none;
+ background-color: #f5f5f5;
+ border-radius: 4px;
}
-.nav-list > li > a,
-.nav-list .nav-header {
- margin-right: -15px;
- margin-left: -15px;
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+.breadcrumb > li {
+ display: inline-block;
}
-.nav-list > li > a {
- padding: 3px 15px;
+.breadcrumb > li + li:before {
+ padding: 0 5px;
+ color: #cccccc;
+ content: "/\00a0";
}
-.nav-list > .active > a,
-.nav-list > .active > a:hover,
-.nav-list > .active > a:focus {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
- background-color: #0088cc;
+.breadcrumb > .active {
+ color: #999999;
}
-.nav-list [class^="icon-"],
-.nav-list [class*=" icon-"] {
- margin-right: 2px;
+.pagination {
+ display: inline-block;
+ padding-left: 0;
+ margin: 20px 0;
+ border-radius: 4px;
}
-.nav-list .divider {
- *width: 100%;
- height: 1px;
- margin: 9px 1px;
- *margin: -5px 0 5px;
- overflow: hidden;
- background-color: #e5e5e5;
- border-bottom: 1px solid #ffffff;
+.pagination > li {
+ display: inline;
}
-.nav-tabs,
-.nav-pills {
- *zoom: 1;
+.pagination > li > a,
+.pagination > li > span {
+ position: relative;
+ float: left;
+ padding: 6px 12px;
+ margin-left: -1px;
+ line-height: 1.428571429;
+ text-decoration: none;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
}
-.nav-tabs:before,
-.nav-pills:before,
-.nav-tabs:after,
-.nav-pills:after {
- display: table;
- line-height: 0;
- content: "";
+.pagination > li:first-child > a,
+.pagination > li:first-child > span {
+ margin-left: 0;
+ border-bottom-left-radius: 4px;
+ border-top-left-radius: 4px;
}
-.nav-tabs:after,
-.nav-pills:after {
- clear: both;
+.pagination > li:last-child > a,
+.pagination > li:last-child > span {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
}
-.nav-tabs > li,
-.nav-pills > li {
- float: left;
+.pagination > li > a:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+ background-color: #eeeeee;
}
-.nav-tabs > li > a,
-.nav-pills > li > a {
- padding-right: 12px;
- padding-left: 12px;
- margin-right: 2px;
- line-height: 14px;
+.pagination > .active > a,
+.pagination > .active > span,
+.pagination > .active > a:hover,
+.pagination > .active > span:hover,
+.pagination > .active > a:focus,
+.pagination > .active > span:focus {
+ z-index: 2;
+ color: #ffffff;
+ cursor: default;
+ background-color: #428bca;
+ border-color: #428bca;
}
-.nav-tabs {
- border-bottom: 1px solid #ddd;
+.pagination > .disabled > span,
+.pagination > .disabled > a,
+.pagination > .disabled > a:hover,
+.pagination > .disabled > a:focus {
+ color: #999999;
+ cursor: not-allowed;
+ background-color: #ffffff;
+ border-color: #dddddd;
}
-.nav-tabs > li {
- margin-bottom: -1px;
+.pagination-lg > li > a,
+.pagination-lg > li > span {
+ padding: 10px 16px;
+ font-size: 18px;
}
-.nav-tabs > li > a {
- padding-top: 8px;
- padding-bottom: 8px;
- line-height: 20px;
- border: 1px solid transparent;
- -webkit-border-radius: 4px 4px 0 0;
- -moz-border-radius: 4px 4px 0 0;
- border-radius: 4px 4px 0 0;
+.pagination-lg > li:first-child > a,
+.pagination-lg > li:first-child > span {
+ border-bottom-left-radius: 6px;
+ border-top-left-radius: 6px;
}
-.nav-tabs > li > a:hover,
-.nav-tabs > li > a:focus {
- border-color: #eeeeee #eeeeee #dddddd;
+.pagination-lg > li:last-child > a,
+.pagination-lg > li:last-child > span {
+ border-top-right-radius: 6px;
+ border-bottom-right-radius: 6px;
}
-.nav-tabs > .active > a,
-.nav-tabs > .active > a:hover,
-.nav-tabs > .active > a:focus {
- color: #555555;
- cursor: default;
- background-color: #ffffff;
- border: 1px solid #ddd;
- border-bottom-color: transparent;
+.pagination-sm > li > a,
+.pagination-sm > li > span {
+ padding: 5px 10px;
+ font-size: 12px;
}
-.nav-pills > li > a {
- padding-top: 8px;
- padding-bottom: 8px;
- margin-top: 2px;
- margin-bottom: 2px;
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- border-radius: 5px;
+.pagination-sm > li:first-child > a,
+.pagination-sm > li:first-child > span {
+ border-bottom-left-radius: 3px;
+ border-top-left-radius: 3px;
}
-.nav-pills > .active > a,
-.nav-pills > .active > a:hover,
-.nav-pills > .active > a:focus {
- color: #ffffff;
- background-color: #0088cc;
+.pagination-sm > li:last-child > a,
+.pagination-sm > li:last-child > span {
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
}
-.nav-stacked > li {
- float: none;
+.pager {
+ padding-left: 0;
+ margin: 20px 0;
+ text-align: center;
+ list-style: none;
}
-.nav-stacked > li > a {
- margin-right: 0;
+.pager:before,
+.pager:after {
+ display: table;
+ content: " ";
}
-.nav-tabs.nav-stacked {
- border-bottom: 0;
+.pager:after {
+ clear: both;
}
-.nav-tabs.nav-stacked > li > a {
- border: 1px solid #ddd;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
+.pager:before,
+.pager:after {
+ display: table;
+ content: " ";
}
-.nav-tabs.nav-stacked > li:first-child > a {
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
- -webkit-border-top-left-radius: 4px;
- border-top-left-radius: 4px;
- -moz-border-radius-topright: 4px;
- -moz-border-radius-topleft: 4px;
+.pager:after {
+ clear: both;
}
-.nav-tabs.nav-stacked > li:last-child > a {
- -webkit-border-bottom-right-radius: 4px;
- border-bottom-right-radius: 4px;
- -webkit-border-bottom-left-radius: 4px;
- border-bottom-left-radius: 4px;
- -moz-border-radius-bottomright: 4px;
- -moz-border-radius-bottomleft: 4px;
+.pager li {
+ display: inline;
}
-.nav-tabs.nav-stacked > li > a:hover,
-.nav-tabs.nav-stacked > li > a:focus {
- z-index: 2;
- border-color: #ddd;
+.pager li > a,
+.pager li > span {
+ display: inline-block;
+ padding: 5px 14px;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 15px;
}
-.nav-pills.nav-stacked > li > a {
- margin-bottom: 3px;
+.pager li > a:hover,
+.pager li > a:focus {
+ text-decoration: none;
+ background-color: #eeeeee;
}
-.nav-pills.nav-stacked > li:last-child > a {
- margin-bottom: 1px;
+.pager .next > a,
+.pager .next > span {
+ float: right;
}
-.nav-tabs .dropdown-menu {
- -webkit-border-radius: 0 0 6px 6px;
- -moz-border-radius: 0 0 6px 6px;
- border-radius: 0 0 6px 6px;
+.pager .previous > a,
+.pager .previous > span {
+ float: left;
}
-.nav-pills .dropdown-menu {
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+ color: #999999;
+ cursor: not-allowed;
+ background-color: #ffffff;
}
-.nav .dropdown-toggle .caret {
- margin-top: 6px;
- border-top-color: #0088cc;
- border-bottom-color: #0088cc;
+.label {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: bold;
+ line-height: 1;
+ color: #ffffff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em;
}
-.nav .dropdown-toggle:hover .caret,
-.nav .dropdown-toggle:focus .caret {
- border-top-color: #005580;
- border-bottom-color: #005580;
+.label[href]:hover,
+.label[href]:focus {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
}
-/* move down carets for tabs */
+.label:empty {
+ display: none;
+}
-.nav-tabs .dropdown-toggle .caret {
- margin-top: 8px;
+.label-default {
+ background-color: #999999;
}
-.nav .active .dropdown-toggle .caret {
- border-top-color: #fff;
- border-bottom-color: #fff;
+.label-default[href]:hover,
+.label-default[href]:focus {
+ background-color: #808080;
}
-.nav-tabs .active .dropdown-toggle .caret {
- border-top-color: #555555;
- border-bottom-color: #555555;
+.label-primary {
+ background-color: #428bca;
}
-.nav > .dropdown.active > a:hover,
-.nav > .dropdown.active > a:focus {
- cursor: pointer;
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+ background-color: #3071a9;
}
-.nav-tabs .open .dropdown-toggle,
-.nav-pills .open .dropdown-toggle,
-.nav > li.dropdown.open.active > a:hover,
-.nav > li.dropdown.open.active > a:focus {
- color: #ffffff;
- background-color: #999999;
- border-color: #999999;
+.label-success {
+ background-color: #5cb85c;
}
-.nav li.dropdown.open .caret,
-.nav li.dropdown.open.active .caret,
-.nav li.dropdown.open a:hover .caret,
-.nav li.dropdown.open a:focus .caret {
- border-top-color: #ffffff;
- border-bottom-color: #ffffff;
- opacity: 1;
- filter: alpha(opacity=100);
+.label-success[href]:hover,
+.label-success[href]:focus {
+ background-color: #449d44;
}
-.tabs-stacked .open > a:hover,
-.tabs-stacked .open > a:focus {
- border-color: #999999;
+.label-info {
+ background-color: #5bc0de;
}
-.tabbable {
- *zoom: 1;
+.label-info[href]:hover,
+.label-info[href]:focus {
+ background-color: #31b0d5;
}
-.tabbable:before,
-.tabbable:after {
- display: table;
- line-height: 0;
- content: "";
+.label-warning {
+ background-color: #f0ad4e;
}
-.tabbable:after {
- clear: both;
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+ background-color: #ec971f;
}
-.tab-content {
- overflow: auto;
+.label-danger {
+ background-color: #d9534f;
}
-.tabs-below > .nav-tabs,
-.tabs-right > .nav-tabs,
-.tabs-left > .nav-tabs {
- border-bottom: 0;
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+ background-color: #c9302c;
}
-.tab-content > .tab-pane,
-.pill-content > .pill-pane {
- display: none;
+.badge {
+ display: inline-block;
+ min-width: 10px;
+ padding: 3px 7px;
+ font-size: 12px;
+ font-weight: bold;
+ line-height: 1;
+ color: #ffffff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ background-color: #999999;
+ border-radius: 10px;
}
-.tab-content > .active,
-.pill-content > .active {
- display: block;
+.badge:empty {
+ display: none;
}
-.tabs-below > .nav-tabs {
- border-top: 1px solid #ddd;
+a.badge:hover,
+a.badge:focus {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
}
-.tabs-below > .nav-tabs > li {
- margin-top: -1px;
- margin-bottom: 0;
+.btn .badge {
+ position: relative;
+ top: -1px;
}
-.tabs-below > .nav-tabs > li > a {
- -webkit-border-radius: 0 0 4px 4px;
- -moz-border-radius: 0 0 4px 4px;
- border-radius: 0 0 4px 4px;
+a.list-group-item.active > .badge,
+.nav-pills > .active > a > .badge {
+ color: #428bca;
+ background-color: #ffffff;
}
-.tabs-below > .nav-tabs > li > a:hover,
-.tabs-below > .nav-tabs > li > a:focus {
- border-top-color: #ddd;
- border-bottom-color: transparent;
+.nav-pills > li > a > .badge {
+ margin-left: 3px;
}
-.tabs-below > .nav-tabs > .active > a,
-.tabs-below > .nav-tabs > .active > a:hover,
-.tabs-below > .nav-tabs > .active > a:focus {
- border-color: transparent #ddd #ddd #ddd;
+.jumbotron {
+ padding: 30px;
+ margin-bottom: 30px;
+ font-size: 21px;
+ font-weight: 200;
+ line-height: 2.1428571435;
+ color: inherit;
+ background-color: #eeeeee;
}
-.tabs-left > .nav-tabs > li,
-.tabs-right > .nav-tabs > li {
- float: none;
+.jumbotron h1 {
+ line-height: 1;
+ color: inherit;
}
-.tabs-left > .nav-tabs > li > a,
-.tabs-right > .nav-tabs > li > a {
- min-width: 74px;
- margin-right: 0;
- margin-bottom: 3px;
+.jumbotron p {
+ line-height: 1.4;
}
-.tabs-left > .nav-tabs {
- float: left;
- margin-right: 19px;
- border-right: 1px solid #ddd;
+.container .jumbotron {
+ border-radius: 6px;
}
-.tabs-left > .nav-tabs > li > a {
- margin-right: -1px;
- -webkit-border-radius: 4px 0 0 4px;
- -moz-border-radius: 4px 0 0 4px;
- border-radius: 4px 0 0 4px;
+@media screen and (min-width: 768px) {
+ .jumbotron {
+ padding-top: 48px;
+ padding-bottom: 48px;
+ }
+ .container .jumbotron {
+ padding-right: 60px;
+ padding-left: 60px;
+ }
+ .jumbotron h1 {
+ font-size: 63px;
+ }
}
-.tabs-left > .nav-tabs > li > a:hover,
-.tabs-left > .nav-tabs > li > a:focus {
- border-color: #eeeeee #dddddd #eeeeee #eeeeee;
+.thumbnail {
+ display: inline-block;
+ display: block;
+ height: auto;
+ max-width: 100%;
+ padding: 4px;
+ line-height: 1.428571429;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 4px;
+ -webkit-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
}
-.tabs-left > .nav-tabs .active > a,
-.tabs-left > .nav-tabs .active > a:hover,
-.tabs-left > .nav-tabs .active > a:focus {
- border-color: #ddd transparent #ddd #ddd;
- *border-right-color: #ffffff;
+.thumbnail > img {
+ display: block;
+ height: auto;
+ max-width: 100%;
}
-.tabs-right > .nav-tabs {
- float: right;
- margin-left: 19px;
- border-left: 1px solid #ddd;
+a.thumbnail:hover,
+a.thumbnail:focus {
+ border-color: #428bca;
}
-.tabs-right > .nav-tabs > li > a {
- margin-left: -1px;
- -webkit-border-radius: 0 4px 4px 0;
- -moz-border-radius: 0 4px 4px 0;
- border-radius: 0 4px 4px 0;
+.thumbnail > img {
+ margin-right: auto;
+ margin-left: auto;
}
-.tabs-right > .nav-tabs > li > a:hover,
-.tabs-right > .nav-tabs > li > a:focus {
- border-color: #eeeeee #eeeeee #eeeeee #dddddd;
+.thumbnail .caption {
+ padding: 9px;
+ color: #333333;
}
-.tabs-right > .nav-tabs .active > a,
-.tabs-right > .nav-tabs .active > a:hover,
-.tabs-right > .nav-tabs .active > a:focus {
- border-color: #ddd #ddd #ddd transparent;
- *border-left-color: #ffffff;
+.alert {
+ padding: 15px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+ border-radius: 4px;
}
-.nav > .disabled > a {
- color: #999999;
+.alert h4 {
+ margin-top: 0;
+ color: inherit;
}
-.nav > .disabled > a:hover,
-.nav > .disabled > a:focus {
- text-decoration: none;
- cursor: default;
- background-color: transparent;
+.alert .alert-link {
+ font-weight: bold;
}
-.navbar {
- *position: relative;
- *z-index: 2;
- margin-bottom: 20px;
- overflow: visible;
+.alert > p,
+.alert > ul {
+ margin-bottom: 0;
}
-.navbar-inner {
- min-height: 40px;
- padding-right: 20px;
- padding-left: 20px;
- background-color: #fafafa;
- background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));
- background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2);
- background-image: -o-linear-gradient(top, #ffffff, #f2f2f2);
- background-image: linear-gradient(to bottom, #ffffff, #f2f2f2);
- background-repeat: repeat-x;
- border: 1px solid #d4d4d4;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
- *zoom: 1;
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
-}
-
-.navbar-inner:before,
-.navbar-inner:after {
- display: table;
- line-height: 0;
- content: "";
+.alert > p + p {
+ margin-top: 5px;
}
-.navbar-inner:after {
- clear: both;
+.alert-dismissable {
+ padding-right: 35px;
}
-.navbar .container {
- width: auto;
+.alert-dismissable .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ color: inherit;
}
-.nav-collapse.collapse {
- height: auto;
- overflow: visible;
+.alert-success {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
}
-.navbar .brand {
- display: block;
- float: left;
- padding: 10px 20px 10px;
- margin-left: -20px;
- font-size: 20px;
- font-weight: 200;
- color: #777777;
- text-shadow: 0 1px 0 #ffffff;
+.alert-success hr {
+ border-top-color: #c9e2b3;
}
-.navbar .brand:hover,
-.navbar .brand:focus {
- text-decoration: none;
+.alert-success .alert-link {
+ color: #356635;
}
-.navbar-text {
- margin-bottom: 0;
- line-height: 40px;
- color: #777777;
+.alert-info {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
}
-.navbar-link {
- color: #777777;
+.alert-info hr {
+ border-top-color: #a6e1ec;
}
-.navbar-link:hover,
-.navbar-link:focus {
- color: #333333;
+.alert-info .alert-link {
+ color: #2d6987;
}
-.navbar .divider-vertical {
- height: 40px;
- margin: 0 9px;
- border-right: 1px solid #ffffff;
- border-left: 1px solid #f2f2f2;
+.alert-warning {
+ color: #c09853;
+ background-color: #fcf8e3;
+ border-color: #fbeed5;
}
-.navbar .btn,
-.navbar .btn-group {
- margin-top: 5px;
+.alert-warning hr {
+ border-top-color: #f8e5be;
}
-.navbar .btn-group .btn,
-.navbar .input-prepend .btn,
-.navbar .input-append .btn,
-.navbar .input-prepend .btn-group,
-.navbar .input-append .btn-group {
- margin-top: 0;
+.alert-warning .alert-link {
+ color: #a47e3c;
}
-.navbar-form {
- margin-bottom: 0;
- *zoom: 1;
+.alert-danger {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #eed3d7;
}
-.navbar-form:before,
-.navbar-form:after {
- display: table;
- line-height: 0;
- content: "";
+.alert-danger hr {
+ border-top-color: #e6c1c7;
}
-.navbar-form:after {
- clear: both;
+.alert-danger .alert-link {
+ color: #953b39;
}
-.navbar-form input,
-.navbar-form select,
-.navbar-form .radio,
-.navbar-form .checkbox {
- margin-top: 5px;
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
}
-.navbar-form input,
-.navbar-form select,
-.navbar-form .btn {
- display: inline-block;
- margin-bottom: 0;
+@-moz-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
}
-.navbar-form input[type="image"],
-.navbar-form input[type="checkbox"],
-.navbar-form input[type="radio"] {
- margin-top: 3px;
+@-o-keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+ to {
+ background-position: 40px 0;
+ }
}
-.navbar-form .input-append,
-.navbar-form .input-prepend {
- margin-top: 5px;
- white-space: nowrap;
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
}
-.navbar-form .input-append input,
-.navbar-form .input-prepend input {
- margin-top: 0;
+.progress {
+ height: 20px;
+ margin-bottom: 20px;
+ overflow: hidden;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
}
-.navbar-search {
- position: relative;
+.progress-bar {
float: left;
- margin-top: 5px;
- margin-bottom: 0;
+ width: 0;
+ height: 100%;
+ font-size: 12px;
+ color: #ffffff;
+ text-align: center;
+ background-color: #428bca;
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -webkit-transition: width 0.6s ease;
+ transition: width 0.6s ease;
}
-.navbar-search .search-query {
- padding: 4px 14px;
- margin-bottom: 0;
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-size: 13px;
- font-weight: normal;
- line-height: 1;
- -webkit-border-radius: 15px;
- -moz-border-radius: 15px;
- border-radius: 15px;
+.progress-striped .progress-bar {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-size: 40px 40px;
}
-.navbar-static-top {
- position: static;
- margin-bottom: 0;
+.progress.active .progress-bar {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -moz-animation: progress-bar-stripes 2s linear infinite;
+ -ms-animation: progress-bar-stripes 2s linear infinite;
+ -o-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
}
-.navbar-static-top .navbar-inner {
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
+.progress-bar-success {
+ background-color: #5cb85c;
}
-.navbar-fixed-top,
-.navbar-fixed-bottom {
- position: fixed;
- right: 0;
- left: 0;
- z-index: 1030;
- margin-bottom: 0;
+.progress-striped .progress-bar-success {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
-.navbar-fixed-top .navbar-inner,
-.navbar-static-top .navbar-inner {
- border-width: 0 0 1px;
+.progress-bar-info {
+ background-color: #5bc0de;
}
-.navbar-fixed-bottom .navbar-inner {
- border-width: 1px 0 0;
+.progress-striped .progress-bar-info {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
-.navbar-fixed-top .navbar-inner,
-.navbar-fixed-bottom .navbar-inner {
- padding-right: 0;
- padding-left: 0;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
+.progress-bar-warning {
+ background-color: #f0ad4e;
}
-.navbar-static-top .container,
-.navbar-fixed-top .container,
-.navbar-fixed-bottom .container {
- width: 940px;
+.progress-striped .progress-bar-warning {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
-.navbar-fixed-top {
- top: 0;
+.progress-bar-danger {
+ background-color: #d9534f;
}
-.navbar-fixed-top .navbar-inner,
-.navbar-static-top .navbar-inner {
- -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
- box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
+.progress-striped .progress-bar-danger {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
-.navbar-fixed-bottom {
- bottom: 0;
+.media,
+.media-body {
+ overflow: hidden;
+ zoom: 1;
}
-.navbar-fixed-bottom .navbar-inner {
- -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
- box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
+.media,
+.media .media {
+ margin-top: 15px;
}
-.navbar .nav {
- position: relative;
- left: 0;
- display: block;
- float: left;
- margin: 0 10px 0 0;
+.media:first-child {
+ margin-top: 0;
}
-.navbar .nav.pull-right {
- float: right;
- margin-right: 0;
+.media-object {
+ display: block;
}
-.navbar .nav > li {
- float: left;
+.media-heading {
+ margin: 0 0 5px;
}
-.navbar .nav > li > a {
- float: none;
- padding: 10px 15px 10px;
- color: #777777;
- text-decoration: none;
- text-shadow: 0 1px 0 #ffffff;
+.media > .pull-left {
+ margin-right: 10px;
}
-.navbar .nav .dropdown-toggle .caret {
- margin-top: 8px;
+.media > .pull-right {
+ margin-left: 10px;
}
-.navbar .nav > li > a:focus,
-.navbar .nav > li > a:hover {
- color: #333333;
- text-decoration: none;
- background-color: transparent;
+.media-list {
+ padding-left: 0;
+ list-style: none;
}
-.navbar .nav > .active > a,
-.navbar .nav > .active > a:hover,
-.navbar .nav > .active > a:focus {
- color: #555555;
- text-decoration: none;
- background-color: #e5e5e5;
- -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
- -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
- box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+.list-group {
+ padding-left: 0;
+ margin-bottom: 20px;
}
-.navbar .btn-navbar {
- display: none;
- float: right;
- padding: 7px 10px;
- margin-right: 5px;
- margin-left: 5px;
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #ededed;
- *background-color: #e5e5e5;
- background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));
- background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5);
- background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5);
- background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5);
- background-repeat: repeat-x;
- border-color: #e5e5e5 #e5e5e5 #bfbfbf;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
- -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
- -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+.list-group-item {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
+ margin-bottom: -1px;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
}
-.navbar .btn-navbar:hover,
-.navbar .btn-navbar:focus,
-.navbar .btn-navbar:active,
-.navbar .btn-navbar.active,
-.navbar .btn-navbar.disabled,
-.navbar .btn-navbar[disabled] {
- color: #ffffff;
- background-color: #e5e5e5;
- *background-color: #d9d9d9;
+.list-group-item:first-child {
+ border-top-right-radius: 4px;
+ border-top-left-radius: 4px;
}
-.navbar .btn-navbar:active,
-.navbar .btn-navbar.active {
- background-color: #cccccc \9;
+.list-group-item:last-child {
+ margin-bottom: 0;
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
}
-.navbar .btn-navbar .icon-bar {
- display: block;
- width: 18px;
- height: 2px;
- background-color: #f5f5f5;
- -webkit-border-radius: 1px;
- -moz-border-radius: 1px;
- border-radius: 1px;
- -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
- -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
- box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+.list-group-item > .badge {
+ float: right;
}
-.btn-navbar .icon-bar + .icon-bar {
- margin-top: 3px;
+.list-group-item > .badge + .badge {
+ margin-right: 5px;
}
-.navbar .nav > li > .dropdown-menu:before {
- position: absolute;
- top: -7px;
- left: 9px;
- display: inline-block;
- border-right: 7px solid transparent;
- border-bottom: 7px solid #ccc;
- border-left: 7px solid transparent;
- border-bottom-color: rgba(0, 0, 0, 0.2);
- content: '';
+a.list-group-item {
+ color: #555555;
}
-.navbar .nav > li > .dropdown-menu:after {
- position: absolute;
- top: -6px;
- left: 10px;
- display: inline-block;
- border-right: 6px solid transparent;
- border-bottom: 6px solid #ffffff;
- border-left: 6px solid transparent;
- content: '';
+a.list-group-item .list-group-item-heading {
+ color: #333333;
}
-.navbar-fixed-bottom .nav > li > .dropdown-menu:before {
- top: auto;
- bottom: -7px;
- border-top: 7px solid #ccc;
- border-bottom: 0;
- border-top-color: rgba(0, 0, 0, 0.2);
+a.list-group-item:hover,
+a.list-group-item:focus {
+ text-decoration: none;
+ background-color: #f5f5f5;
}
-.navbar-fixed-bottom .nav > li > .dropdown-menu:after {
- top: auto;
- bottom: -6px;
- border-top: 6px solid #ffffff;
- border-bottom: 0;
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+ z-index: 2;
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #428bca;
}
-.navbar .nav li.dropdown > a:hover .caret,
-.navbar .nav li.dropdown > a:focus .caret {
- border-top-color: #333333;
- border-bottom-color: #333333;
+.list-group-item.active .list-group-item-heading,
+.list-group-item.active:hover .list-group-item-heading,
+.list-group-item.active:focus .list-group-item-heading {
+ color: inherit;
}
-.navbar .nav li.dropdown.open > .dropdown-toggle,
-.navbar .nav li.dropdown.active > .dropdown-toggle,
-.navbar .nav li.dropdown.open.active > .dropdown-toggle {
- color: #555555;
- background-color: #e5e5e5;
+.list-group-item.active .list-group-item-text,
+.list-group-item.active:hover .list-group-item-text,
+.list-group-item.active:focus .list-group-item-text {
+ color: #e1edf7;
}
-.navbar .nav li.dropdown > .dropdown-toggle .caret {
- border-top-color: #777777;
- border-bottom-color: #777777;
+.list-group-item-heading {
+ margin-top: 0;
+ margin-bottom: 5px;
}
-.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
-.navbar .nav li.dropdown.active > .dropdown-toggle .caret,
-.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret {
- border-top-color: #555555;
- border-bottom-color: #555555;
+.list-group-item-text {
+ margin-bottom: 0;
+ line-height: 1.3;
}
-.navbar .pull-right > li > .dropdown-menu,
-.navbar .nav > li > .dropdown-menu.pull-right {
- right: 0;
- left: auto;
+.panel {
+ margin-bottom: 20px;
+ background-color: #ffffff;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
}
-.navbar .pull-right > li > .dropdown-menu:before,
-.navbar .nav > li > .dropdown-menu.pull-right:before {
- right: 12px;
- left: auto;
+.panel-body {
+ padding: 15px;
}
-.navbar .pull-right > li > .dropdown-menu:after,
-.navbar .nav > li > .dropdown-menu.pull-right:after {
- right: 13px;
- left: auto;
+.panel-body:before,
+.panel-body:after {
+ display: table;
+ content: " ";
}
-.navbar .pull-right > li > .dropdown-menu .dropdown-menu,
-.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu {
- right: 100%;
- left: auto;
- margin-right: -1px;
- margin-left: 0;
- -webkit-border-radius: 6px 0 6px 6px;
- -moz-border-radius: 6px 0 6px 6px;
- border-radius: 6px 0 6px 6px;
-}
-
-.navbar-inverse .navbar-inner {
- background-color: #1b1b1b;
- background-image: -moz-linear-gradient(top, #222222, #111111);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));
- background-image: -webkit-linear-gradient(top, #222222, #111111);
- background-image: -o-linear-gradient(top, #222222, #111111);
- background-image: linear-gradient(to bottom, #222222, #111111);
- background-repeat: repeat-x;
- border-color: #252525;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
+.panel-body:after {
+ clear: both;
}
-.navbar-inverse .brand,
-.navbar-inverse .nav > li > a {
- color: #999999;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+.panel-body:before,
+.panel-body:after {
+ display: table;
+ content: " ";
}
-.navbar-inverse .brand:hover,
-.navbar-inverse .nav > li > a:hover,
-.navbar-inverse .brand:focus,
-.navbar-inverse .nav > li > a:focus {
- color: #ffffff;
+.panel-body:after {
+ clear: both;
}
-.navbar-inverse .brand {
- color: #999999;
+.panel > .list-group {
+ margin-bottom: 0;
}
-.navbar-inverse .navbar-text {
- color: #999999;
+.panel > .list-group .list-group-item {
+ border-width: 1px 0;
}
-.navbar-inverse .nav > li > a:focus,
-.navbar-inverse .nav > li > a:hover {
- color: #ffffff;
- background-color: transparent;
+.panel > .list-group .list-group-item:first-child {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
}
-.navbar-inverse .nav .active > a,
-.navbar-inverse .nav .active > a:hover,
-.navbar-inverse .nav .active > a:focus {
- color: #ffffff;
- background-color: #111111;
+.panel > .list-group .list-group-item:last-child {
+ border-bottom: 0;
}
-.navbar-inverse .navbar-link {
- color: #999999;
+.panel-heading + .list-group .list-group-item:first-child {
+ border-top-width: 0;
}
-.navbar-inverse .navbar-link:hover,
-.navbar-inverse .navbar-link:focus {
- color: #ffffff;
+.panel > .table {
+ margin-bottom: 0;
}
-.navbar-inverse .divider-vertical {
- border-right-color: #222222;
- border-left-color: #111111;
+.panel > .panel-body + .table {
+ border-top: 1px solid #dddddd;
}
-.navbar-inverse .nav li.dropdown.open > .dropdown-toggle,
-.navbar-inverse .nav li.dropdown.active > .dropdown-toggle,
-.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle {
- color: #ffffff;
- background-color: #111111;
+.panel-heading {
+ padding: 10px 15px;
+ border-bottom: 1px solid transparent;
+ border-top-right-radius: 3px;
+ border-top-left-radius: 3px;
}
-.navbar-inverse .nav li.dropdown > a:hover .caret,
-.navbar-inverse .nav li.dropdown > a:focus .caret {
- border-top-color: #ffffff;
- border-bottom-color: #ffffff;
+.panel-title {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: 16px;
}
-.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret {
- border-top-color: #999999;
- border-bottom-color: #999999;
+.panel-title > a {
+ color: inherit;
}
-.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret,
-.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret,
-.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret {
- border-top-color: #ffffff;
- border-bottom-color: #ffffff;
+.panel-footer {
+ padding: 10px 15px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #dddddd;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
}
-.navbar-inverse .navbar-search .search-query {
- color: #ffffff;
- background-color: #515151;
- border-color: #111111;
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
- -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
- box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
- -webkit-transition: none;
- -moz-transition: none;
- -o-transition: none;
- transition: none;
-}
-
-.navbar-inverse .navbar-search .search-query:-moz-placeholder {
- color: #cccccc;
+.panel-group .panel {
+ margin-bottom: 0;
+ overflow: hidden;
+ border-radius: 4px;
}
-.navbar-inverse .navbar-search .search-query:-ms-input-placeholder {
- color: #cccccc;
+.panel-group .panel + .panel {
+ margin-top: 5px;
}
-.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder {
- color: #cccccc;
+.panel-group .panel-heading {
+ border-bottom: 0;
}
-.navbar-inverse .navbar-search .search-query:focus,
-.navbar-inverse .navbar-search .search-query.focused {
- padding: 5px 15px;
- color: #333333;
- text-shadow: 0 1px 0 #ffffff;
- background-color: #ffffff;
- border: 0;
- outline: 0;
- -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
- -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
- box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+.panel-group .panel-heading + .panel-collapse .panel-body {
+ border-top: 1px solid #dddddd;
}
-.navbar-inverse .btn-navbar {
- color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #0e0e0e;
- *background-color: #040404;
- background-image: -moz-linear-gradient(top, #151515, #040404);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404));
- background-image: -webkit-linear-gradient(top, #151515, #040404);
- background-image: -o-linear-gradient(top, #151515, #040404);
- background-image: linear-gradient(to bottom, #151515, #040404);
- background-repeat: repeat-x;
- border-color: #040404 #040404 #000000;
- border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+.panel-group .panel-footer {
+ border-top: 0;
}
-.navbar-inverse .btn-navbar:hover,
-.navbar-inverse .btn-navbar:focus,
-.navbar-inverse .btn-navbar:active,
-.navbar-inverse .btn-navbar.active,
-.navbar-inverse .btn-navbar.disabled,
-.navbar-inverse .btn-navbar[disabled] {
- color: #ffffff;
- background-color: #040404;
- *background-color: #000000;
+.panel-group .panel-footer + .panel-collapse .panel-body {
+ border-bottom: 1px solid #dddddd;
}
-.navbar-inverse .btn-navbar:active,
-.navbar-inverse .btn-navbar.active {
- background-color: #000000 \9;
+.panel-default {
+ border-color: #dddddd;
}
-.breadcrumb {
- padding: 8px 15px;
- margin: 0 0 20px;
- list-style: none;
+.panel-default > .panel-heading {
+ color: #333333;
background-color: #f5f5f5;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
+ border-color: #dddddd;
}
-.breadcrumb > li {
- display: inline-block;
- *display: inline;
- text-shadow: 0 1px 0 #ffffff;
- *zoom: 1;
+.panel-default > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #dddddd;
}
-.breadcrumb > li > .divider {
- padding: 0 5px;
- color: #ccc;
+.panel-default > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #dddddd;
}
-.breadcrumb > .active {
- color: #999999;
+.panel-primary {
+ border-color: #428bca;
}
-.pagination {
- margin: 20px 0;
+.panel-primary > .panel-heading {
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #428bca;
}
-.pagination ul {
- display: inline-block;
- *display: inline;
- margin-bottom: 0;
- margin-left: 0;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- *zoom: 1;
- -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+.panel-primary > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #428bca;
}
-.pagination ul > li {
- display: inline;
+.panel-primary > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #428bca;
}
-.pagination ul > li > a,
-.pagination ul > li > span {
- float: left;
- padding: 4px 12px;
- line-height: 20px;
- text-decoration: none;
- background-color: #ffffff;
- border: 1px solid #dddddd;
- border-left-width: 0;
+.panel-success {
+ border-color: #d6e9c6;
}
-.pagination ul > li > a:hover,
-.pagination ul > li > a:focus,
-.pagination ul > .active > a,
-.pagination ul > .active > span {
- background-color: #f5f5f5;
+.panel-success > .panel-heading {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
}
-.pagination ul > .active > a,
-.pagination ul > .active > span {
- color: #999999;
- cursor: default;
+.panel-success > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #d6e9c6;
}
-.pagination ul > .disabled > span,
-.pagination ul > .disabled > a,
-.pagination ul > .disabled > a:hover,
-.pagination ul > .disabled > a:focus {
- color: #999999;
- cursor: default;
- background-color: transparent;
+.panel-success > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #d6e9c6;
+}
+
+.panel-warning {
+ border-color: #fbeed5;
}
-.pagination ul > li:first-child > a,
-.pagination ul > li:first-child > span {
- border-left-width: 1px;
- -webkit-border-bottom-left-radius: 4px;
- border-bottom-left-radius: 4px;
- -webkit-border-top-left-radius: 4px;
- border-top-left-radius: 4px;
- -moz-border-radius-bottomleft: 4px;
- -moz-border-radius-topleft: 4px;
+.panel-warning > .panel-heading {
+ color: #c09853;
+ background-color: #fcf8e3;
+ border-color: #fbeed5;
}
-.pagination ul > li:last-child > a,
-.pagination ul > li:last-child > span {
- -webkit-border-top-right-radius: 4px;
- border-top-right-radius: 4px;
- -webkit-border-bottom-right-radius: 4px;
- border-bottom-right-radius: 4px;
- -moz-border-radius-topright: 4px;
- -moz-border-radius-bottomright: 4px;
+.panel-warning > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #fbeed5;
}
-.pagination-centered {
- text-align: center;
+.panel-warning > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #fbeed5;
}
-.pagination-right {
- text-align: right;
+.panel-danger {
+ border-color: #eed3d7;
}
-.pagination-large ul > li > a,
-.pagination-large ul > li > span {
- padding: 11px 19px;
- font-size: 17.5px;
+.panel-danger > .panel-heading {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #eed3d7;
}
-.pagination-large ul > li:first-child > a,
-.pagination-large ul > li:first-child > span {
- -webkit-border-bottom-left-radius: 6px;
- border-bottom-left-radius: 6px;
- -webkit-border-top-left-radius: 6px;
- border-top-left-radius: 6px;
- -moz-border-radius-bottomleft: 6px;
- -moz-border-radius-topleft: 6px;
+.panel-danger > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #eed3d7;
}
-.pagination-large ul > li:last-child > a,
-.pagination-large ul > li:last-child > span {
- -webkit-border-top-right-radius: 6px;
- border-top-right-radius: 6px;
- -webkit-border-bottom-right-radius: 6px;
- border-bottom-right-radius: 6px;
- -moz-border-radius-topright: 6px;
- -moz-border-radius-bottomright: 6px;
+.panel-danger > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #eed3d7;
}
-.pagination-mini ul > li:first-child > a,
-.pagination-small ul > li:first-child > a,
-.pagination-mini ul > li:first-child > span,
-.pagination-small ul > li:first-child > span {
- -webkit-border-bottom-left-radius: 3px;
- border-bottom-left-radius: 3px;
- -webkit-border-top-left-radius: 3px;
- border-top-left-radius: 3px;
- -moz-border-radius-bottomleft: 3px;
- -moz-border-radius-topleft: 3px;
+.panel-info {
+ border-color: #bce8f1;
}
-.pagination-mini ul > li:last-child > a,
-.pagination-small ul > li:last-child > a,
-.pagination-mini ul > li:last-child > span,
-.pagination-small ul > li:last-child > span {
- -webkit-border-top-right-radius: 3px;
- border-top-right-radius: 3px;
- -webkit-border-bottom-right-radius: 3px;
- border-bottom-right-radius: 3px;
- -moz-border-radius-topright: 3px;
- -moz-border-radius-bottomright: 3px;
+.panel-info > .panel-heading {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
}
-.pagination-small ul > li > a,
-.pagination-small ul > li > span {
- padding: 2px 10px;
- font-size: 11.9px;
+.panel-info > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #bce8f1;
}
-.pagination-mini ul > li > a,
-.pagination-mini ul > li > span {
- padding: 0 6px;
- font-size: 10.5px;
+.panel-info > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #bce8f1;
}
-.pager {
- margin: 20px 0;
- text-align: center;
- list-style: none;
- *zoom: 1;
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border: 1px solid #e3e3e3;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
}
-.pager:before,
-.pager:after {
- display: table;
- line-height: 0;
- content: "";
+.well blockquote {
+ border-color: #ddd;
+ border-color: rgba(0, 0, 0, 0.15);
}
-.pager:after {
- clear: both;
+.well-lg {
+ padding: 24px;
+ border-radius: 6px;
}
-.pager li {
- display: inline;
+.well-sm {
+ padding: 9px;
+ border-radius: 3px;
}
-.pager li > a,
-.pager li > span {
- display: inline-block;
- padding: 5px 14px;
- background-color: #fff;
- border: 1px solid #ddd;
- -webkit-border-radius: 15px;
- -moz-border-radius: 15px;
- border-radius: 15px;
+.close {
+ float: right;
+ font-size: 21px;
+ font-weight: bold;
+ line-height: 1;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
}
-.pager li > a:hover,
-.pager li > a:focus {
+.close:hover,
+.close:focus {
+ color: #000000;
text-decoration: none;
- background-color: #f5f5f5;
+ cursor: pointer;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
}
-.pager .next > a,
-.pager .next > span {
- float: right;
+button.close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
}
-.pager .previous > a,
-.pager .previous > span {
- float: left;
+.modal-open {
+ overflow: hidden;
}
-.pager .disabled > a,
-.pager .disabled > a:hover,
-.pager .disabled > a:focus,
-.pager .disabled > span {
- color: #999999;
- cursor: default;
- background-color: #fff;
+body.modal-open,
+.modal-open .navbar-fixed-top,
+.modal-open .navbar-fixed-bottom {
+ margin-right: 15px;
}
-.modal-backdrop {
+.modal {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1040;
- background-color: #000000;
+ display: none;
+ overflow: auto;
+ overflow-y: scroll;
}
-.modal-backdrop.fade {
- opacity: 0;
+.modal.fade .modal-dialog {
+ -webkit-transform: translate(0, -25%);
+ -ms-transform: translate(0, -25%);
+ transform: translate(0, -25%);
+ -webkit-transition: -webkit-transform 0.3s ease-out;
+ -moz-transition: -moz-transform 0.3s ease-out;
+ -o-transition: -o-transform 0.3s ease-out;
+ transition: transform 0.3s ease-out;
}
-.modal-backdrop,
-.modal-backdrop.fade.in {
- opacity: 0.8;
- filter: alpha(opacity=80);
+.modal.in .modal-dialog {
+ -webkit-transform: translate(0, 0);
+ -ms-transform: translate(0, 0);
+ transform: translate(0, 0);
}
-.modal {
- position: fixed;
- top: 10%;
- left: 50%;
+.modal-dialog {
z-index: 1050;
- width: 560px;
- margin-left: -280px;
+ width: auto;
+ padding: 10px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.modal-content {
+ position: relative;
background-color: #ffffff;
- border: 1px solid #999;
- border: 1px solid rgba(0, 0, 0, 0.3);
- *border: 1px solid #999;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
+ border: 1px solid #999999;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
outline: none;
- -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
- -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
- box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding-box;
- background-clip: padding-box;
+ -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ background-clip: padding-box;
+}
+
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1030;
+ background-color: #000000;
}
-.modal.fade {
- top: -25%;
- -webkit-transition: opacity 0.3s linear, top 0.3s ease-out;
- -moz-transition: opacity 0.3s linear, top 0.3s ease-out;
- -o-transition: opacity 0.3s linear, top 0.3s ease-out;
- transition: opacity 0.3s linear, top 0.3s ease-out;
+.modal-backdrop.fade {
+ opacity: 0;
+ filter: alpha(opacity=0);
}
-.modal.fade.in {
- top: 10%;
+.modal-backdrop.in {
+ opacity: 0.5;
+ filter: alpha(opacity=50);
}
.modal-header {
- padding: 9px 15px;
- border-bottom: 1px solid #eee;
+ min-height: 16.428571429px;
+ padding: 15px;
+ border-bottom: 1px solid #e5e5e5;
}
.modal-header .close {
- margin-top: 2px;
+ margin-top: -2px;
}
-.modal-header h3 {
+.modal-title {
margin: 0;
- line-height: 30px;
+ line-height: 1.428571429;
}
.modal-body {
position: relative;
- max-height: 400px;
- padding: 15px;
- overflow-y: auto;
-}
-
-.modal-form {
- margin-bottom: 0;
+ padding: 20px;
}
.modal-footer {
- padding: 14px 15px 15px;
- margin-bottom: 0;
+ padding: 19px 20px 20px;
+ margin-top: 15px;
text-align: right;
- background-color: #f5f5f5;
- border-top: 1px solid #ddd;
- -webkit-border-radius: 0 0 6px 6px;
- -moz-border-radius: 0 0 6px 6px;
- border-radius: 0 0 6px 6px;
- *zoom: 1;
- -webkit-box-shadow: inset 0 1px 0 #ffffff;
- -moz-box-shadow: inset 0 1px 0 #ffffff;
- box-shadow: inset 0 1px 0 #ffffff;
+ border-top: 1px solid #e5e5e5;
+}
+
+.modal-footer:before,
+.modal-footer:after {
+ display: table;
+ content: " ";
+}
+
+.modal-footer:after {
+ clear: both;
}
.modal-footer:before,
.modal-footer:after {
display: table;
- line-height: 0;
- content: "";
+ content: " ";
}
.modal-footer:after {
@@ -5296,11 +5728,25 @@ input[type="submit"].btn.btn-mini {
margin-left: 0;
}
+@media screen and (min-width: 768px) {
+ .modal-dialog {
+ right: auto;
+ left: 50%;
+ width: 600px;
+ padding-top: 30px;
+ padding-bottom: 30px;
+ }
+ .modal-content {
+ -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+ }
+}
+
.tooltip {
position: absolute;
z-index: 1030;
display: block;
- font-size: 11px;
+ font-size: 12px;
line-height: 1.4;
opacity: 0;
filter: alpha(opacity=0);
@@ -5308,8 +5754,8 @@ input[type="submit"].btn.btn-mini {
}
.tooltip.in {
- opacity: 0.8;
- filter: alpha(opacity=80);
+ opacity: 0.9;
+ filter: alpha(opacity=90);
}
.tooltip.top {
@@ -5334,14 +5780,12 @@ input[type="submit"].btn.btn-mini {
.tooltip-inner {
max-width: 200px;
- padding: 8px;
+ padding: 3px 8px;
color: #ffffff;
text-align: center;
text-decoration: none;
background-color: #000000;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
+ border-radius: 4px;
}
.tooltip-arrow {
@@ -5360,6 +5804,20 @@ input[type="submit"].btn.btn-mini {
border-width: 5px 5px 0;
}
+.tooltip.top-left .tooltip-arrow {
+ bottom: 0;
+ left: 5px;
+ border-top-color: #000000;
+ border-width: 5px 5px 0;
+}
+
+.tooltip.top-right .tooltip-arrow {
+ right: 5px;
+ bottom: 0;
+ border-top-color: #000000;
+ border-width: 5px 5px 0;
+}
+
.tooltip.right .tooltip-arrow {
top: 50%;
left: 0;
@@ -5384,6 +5842,20 @@ input[type="submit"].btn.btn-mini {
border-width: 0 5px 5px;
}
+.tooltip.bottom-left .tooltip-arrow {
+ top: 0;
+ left: 5px;
+ border-bottom-color: #000000;
+ border-width: 0 5px 5px;
+}
+
+.tooltip.bottom-right .tooltip-arrow {
+ top: 0;
+ right: 5px;
+ border-bottom-color: #000000;
+ border-width: 0 5px 5px;
+}
+
.popover {
position: absolute;
top: 0;
@@ -5395,17 +5867,12 @@ input[type="submit"].btn.btn-mini {
text-align: left;
white-space: normal;
background-color: #ffffff;
- border: 1px solid #ccc;
+ border: 1px solid #cccccc;
border: 1px solid rgba(0, 0, 0, 0.2);
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
+ border-radius: 6px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding;
- background-clip: padding-box;
+ background-clip: padding-box;
}
.popover.top {
@@ -5432,13 +5899,7 @@ input[type="submit"].btn.btn-mini {
line-height: 18px;
background-color: #f7f7f7;
border-bottom: 1px solid #ebebeb;
- -webkit-border-radius: 5px 5px 0 0;
- -moz-border-radius: 5px 5px 0 0;
- border-radius: 5px 5px 0 0;
-}
-
-.popover-title:empty {
- display: none;
+ border-radius: 5px 5px 0 0;
}
.popover-content {
@@ -5468,7 +5929,7 @@ input[type="submit"].btn.btn-mini {
bottom: -11px;
left: 50%;
margin-left: -11px;
- border-top-color: #999;
+ border-top-color: #999999;
border-top-color: rgba(0, 0, 0, 0.25);
border-bottom-width: 0;
}
@@ -5478,13 +5939,14 @@ input[type="submit"].btn.btn-mini {
margin-left: -10px;
border-top-color: #ffffff;
border-bottom-width: 0;
+ content: " ";
}
.popover.right .arrow {
top: 50%;
left: -11px;
margin-top: -11px;
- border-right-color: #999;
+ border-right-color: #999999;
border-right-color: rgba(0, 0, 0, 0.25);
border-left-width: 0;
}
@@ -5494,13 +5956,14 @@ input[type="submit"].btn.btn-mini {
left: 1px;
border-right-color: #ffffff;
border-left-width: 0;
+ content: " ";
}
.popover.bottom .arrow {
top: -11px;
left: 50%;
margin-left: -11px;
- border-bottom-color: #999;
+ border-bottom-color: #999999;
border-bottom-color: rgba(0, 0, 0, 0.25);
border-top-width: 0;
}
@@ -5510,13 +5973,14 @@ input[type="submit"].btn.btn-mini {
margin-left: -10px;
border-bottom-color: #ffffff;
border-top-width: 0;
+ content: " ";
}
.popover.left .arrow {
top: 50%;
right: -11px;
margin-top: -11px;
- border-left-color: #999;
+ border-left-color: #999999;
border-left-color: rgba(0, 0, 0, 0.25);
border-right-width: 0;
}
@@ -5526,644 +5990,818 @@ input[type="submit"].btn.btn-mini {
bottom: -10px;
border-left-color: #ffffff;
border-right-width: 0;
+ content: " ";
}
-.thumbnails {
- margin-left: -20px;
- list-style: none;
- *zoom: 1;
+.carousel {
+ position: relative;
}
-.thumbnails:before,
-.thumbnails:after {
- display: table;
- line-height: 0;
- content: "";
+.carousel-inner {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
}
-.thumbnails:after {
- clear: both;
+.carousel-inner > .item {
+ position: relative;
+ display: none;
+ -webkit-transition: 0.6s ease-in-out left;
+ transition: 0.6s ease-in-out left;
}
-.row-fluid .thumbnails {
- margin-left: 0;
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+ display: block;
+ height: auto;
+ max-width: 100%;
+ line-height: 1;
}
-.thumbnails > li {
- float: left;
- margin-bottom: 20px;
- margin-left: 20px;
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ display: block;
}
-.thumbnail {
- display: block;
- padding: 4px;
- line-height: 20px;
- border: 1px solid #ddd;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
- -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
- -webkit-transition: all 0.2s ease-in-out;
- -moz-transition: all 0.2s ease-in-out;
- -o-transition: all 0.2s ease-in-out;
- transition: all 0.2s ease-in-out;
+.carousel-inner > .active {
+ left: 0;
}
-a.thumbnail:hover,
-a.thumbnail:focus {
- border-color: #0088cc;
- -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
- -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
- box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
}
-.thumbnail > img {
- display: block;
- max-width: 100%;
- margin-right: auto;
- margin-left: auto;
+.carousel-inner > .next {
+ left: 100%;
}
-.thumbnail .caption {
- padding: 9px;
- color: #555555;
+.carousel-inner > .prev {
+ left: -100%;
}
-.media,
-.media-body {
- overflow: hidden;
- *overflow: visible;
- zoom: 1;
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+ left: 0;
}
-.media,
-.media .media {
- margin-top: 15px;
+.carousel-inner > .active.left {
+ left: -100%;
}
-.media:first-child {
- margin-top: 0;
+.carousel-inner > .active.right {
+ left: 100%;
}
-.media-object {
- display: block;
+.carousel-control {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 15%;
+ font-size: 20px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
+ opacity: 0.5;
+ filter: alpha(opacity=50);
}
-.media-heading {
- margin: 0 0 5px;
+.carousel-control.left {
+ background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001)));
+ background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%));
+ background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%);
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
}
-.media > .pull-left {
- margin-right: 10px;
+.carousel-control.right {
+ right: 0;
+ left: auto;
+ background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5)));
+ background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%));
+ background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%);
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
}
-.media > .pull-right {
- margin-left: 10px;
+.carousel-control:hover,
+.carousel-control:focus {
+ color: #ffffff;
+ text-decoration: none;
+ opacity: 0.9;
+ filter: alpha(opacity=90);
}
-.media-list {
- margin-left: 0;
+.carousel-control .icon-prev,
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ z-index: 5;
+ display: inline-block;
+}
+
+.carousel-control .icon-prev,
+.carousel-control .icon-next {
+ width: 20px;
+ height: 20px;
+ margin-top: -10px;
+ margin-left: -10px;
+ font-family: serif;
+}
+
+.carousel-control .icon-prev:before {
+ content: '\2039';
+}
+
+.carousel-control .icon-next:before {
+ content: '\203a';
+}
+
+.carousel-indicators {
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ z-index: 15;
+ width: 60%;
+ padding-left: 0;
+ margin-left: -30%;
+ text-align: center;
list-style: none;
}
-.label,
-.badge {
+.carousel-indicators li {
display: inline-block;
- padding: 2px 4px;
- font-size: 11.844px;
- font-weight: bold;
- line-height: 14px;
+ width: 10px;
+ height: 10px;
+ margin: 1px;
+ text-indent: -999px;
+ cursor: pointer;
+ border: 1px solid #ffffff;
+ border-radius: 10px;
+}
+
+.carousel-indicators .active {
+ width: 12px;
+ height: 12px;
+ margin: 0;
+ background-color: #ffffff;
+}
+
+.carousel-caption {
+ position: absolute;
+ right: 15%;
+ bottom: 20px;
+ left: 15%;
+ z-index: 10;
+ padding-top: 20px;
+ padding-bottom: 20px;
color: #ffffff;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- white-space: nowrap;
- vertical-align: baseline;
- background-color: #999999;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
-.label {
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
+.carousel-caption .btn {
+ text-shadow: none;
}
-.badge {
- padding-right: 9px;
- padding-left: 9px;
- -webkit-border-radius: 9px;
- -moz-border-radius: 9px;
- border-radius: 9px;
+@media screen and (min-width: 768px) {
+ .carousel-control .icon-prev,
+ .carousel-control .icon-next {
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ margin-left: -15px;
+ font-size: 30px;
+ }
+ .carousel-caption {
+ right: 20%;
+ left: 20%;
+ padding-bottom: 30px;
+ }
+ .carousel-indicators {
+ bottom: 20px;
+ }
}
-.label:empty,
-.badge:empty {
- display: none;
+.clearfix:before,
+.clearfix:after {
+ display: table;
+ content: " ";
}
-a.label:hover,
-a.label:focus,
-a.badge:hover,
-a.badge:focus {
- color: #ffffff;
- text-decoration: none;
- cursor: pointer;
+.clearfix:after {
+ clear: both;
}
-.label-important,
-.badge-important {
- background-color: #b94a48;
+.pull-right {
+ float: right !important;
}
-.label-important[href],
-.badge-important[href] {
- background-color: #953b39;
+.pull-left {
+ float: left !important;
}
-.label-warning,
-.badge-warning {
- background-color: #f89406;
+.hide {
+ display: none !important;
}
-.label-warning[href],
-.badge-warning[href] {
- background-color: #c67605;
+.show {
+ display: block !important;
}
-.label-success,
-.badge-success {
- background-color: #468847;
+.invisible {
+ visibility: hidden;
}
-.label-success[href],
-.badge-success[href] {
- background-color: #356635;
+.text-hide {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
}
-.label-info,
-.badge-info {
- background-color: #3a87ad;
+.affix {
+ position: fixed;
}
-.label-info[href],
-.badge-info[href] {
- background-color: #2d6987;
+@-ms-viewport {
+ width: device-width;
}
-.label-inverse,
-.badge-inverse {
- background-color: #333333;
+@media screen and (max-width: 400px) {
+ @-ms-viewport {
+ width: 320px;
+ }
}
-.label-inverse[href],
-.badge-inverse[href] {
- background-color: #1a1a1a;
+.hidden {
+ display: none !important;
+ visibility: hidden !important;
}
-.btn .label,
-.btn .badge {
- position: relative;
- top: -1px;
+.visible-xs {
+ display: none !important;
}
-.btn-mini .label,
-.btn-mini .badge {
- top: 0;
+tr.visible-xs {
+ display: none !important;
}
-@-webkit-keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
+th.visible-xs,
+td.visible-xs {
+ display: none !important;
+}
+
+@media (max-width: 767px) {
+ .visible-xs {
+ display: block !important;
}
- to {
- background-position: 0 0;
+ tr.visible-xs {
+ display: table-row !important;
+ }
+ th.visible-xs,
+ td.visible-xs {
+ display: table-cell !important;
}
}
-@-moz-keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
+@media (min-width: 768px) and (max-width: 991px) {
+ .visible-xs.visible-sm {
+ display: block !important;
}
- to {
- background-position: 0 0;
+ tr.visible-xs.visible-sm {
+ display: table-row !important;
+ }
+ th.visible-xs.visible-sm,
+ td.visible-xs.visible-sm {
+ display: table-cell !important;
}
}
-@-ms-keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
+@media (min-width: 992px) and (max-width: 1199px) {
+ .visible-xs.visible-md {
+ display: block !important;
}
- to {
- background-position: 0 0;
+ tr.visible-xs.visible-md {
+ display: table-row !important;
+ }
+ th.visible-xs.visible-md,
+ td.visible-xs.visible-md {
+ display: table-cell !important;
}
}
-@-o-keyframes progress-bar-stripes {
- from {
- background-position: 0 0;
+@media (min-width: 1200px) {
+ .visible-xs.visible-lg {
+ display: block !important;
}
- to {
- background-position: 40px 0;
+ tr.visible-xs.visible-lg {
+ display: table-row !important;
+ }
+ th.visible-xs.visible-lg,
+ td.visible-xs.visible-lg {
+ display: table-cell !important;
}
}
-@keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
- }
- to {
- background-position: 0 0;
- }
+.visible-sm {
+ display: none !important;
+}
+
+tr.visible-sm {
+ display: none !important;
+}
+
+th.visible-sm,
+td.visible-sm {
+ display: none !important;
}
-.progress {
- height: 20px;
- margin-bottom: 20px;
- overflow: hidden;
- background-color: #f7f7f7;
- background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
- background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
- background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
- background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
- background-repeat: repeat-x;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+@media (max-width: 767px) {
+ .visible-sm.visible-xs {
+ display: block !important;
+ }
+ tr.visible-sm.visible-xs {
+ display: table-row !important;
+ }
+ th.visible-sm.visible-xs,
+ td.visible-sm.visible-xs {
+ display: table-cell !important;
+ }
}
-.progress .bar {
- float: left;
- width: 0;
- height: 100%;
- font-size: 12px;
- color: #ffffff;
- text-align: center;
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
- background-color: #0e90d2;
- background-image: -moz-linear-gradient(top, #149bdf, #0480be);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
- background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
- background-image: -o-linear-gradient(top, #149bdf, #0480be);
- background-image: linear-gradient(to bottom, #149bdf, #0480be);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
- -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- -webkit-transition: width 0.6s ease;
- -moz-transition: width 0.6s ease;
- -o-transition: width 0.6s ease;
- transition: width 0.6s ease;
+@media (min-width: 768px) and (max-width: 991px) {
+ .visible-sm {
+ display: block !important;
+ }
+ tr.visible-sm {
+ display: table-row !important;
+ }
+ th.visible-sm,
+ td.visible-sm {
+ display: table-cell !important;
+ }
}
-.progress .bar + .bar {
- -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+@media (min-width: 992px) and (max-width: 1199px) {
+ .visible-sm.visible-md {
+ display: block !important;
+ }
+ tr.visible-sm.visible-md {
+ display: table-row !important;
+ }
+ th.visible-sm.visible-md,
+ td.visible-sm.visible-md {
+ display: table-cell !important;
+ }
}
-.progress-striped .bar {
- background-color: #149bdf;
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- -webkit-background-size: 40px 40px;
- -moz-background-size: 40px 40px;
- -o-background-size: 40px 40px;
- background-size: 40px 40px;
+@media (min-width: 1200px) {
+ .visible-sm.visible-lg {
+ display: block !important;
+ }
+ tr.visible-sm.visible-lg {
+ display: table-row !important;
+ }
+ th.visible-sm.visible-lg,
+ td.visible-sm.visible-lg {
+ display: table-cell !important;
+ }
}
-.progress.active .bar {
- -webkit-animation: progress-bar-stripes 2s linear infinite;
- -moz-animation: progress-bar-stripes 2s linear infinite;
- -ms-animation: progress-bar-stripes 2s linear infinite;
- -o-animation: progress-bar-stripes 2s linear infinite;
- animation: progress-bar-stripes 2s linear infinite;
+.visible-md {
+ display: none !important;
}
-.progress-danger .bar,
-.progress .bar-danger {
- background-color: #dd514c;
- background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
- background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
- background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
- background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
+tr.visible-md {
+ display: none !important;
}
-.progress-danger.progress-striped .bar,
-.progress-striped .bar-danger {
- background-color: #ee5f5b;
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+th.visible-md,
+td.visible-md {
+ display: none !important;
}
-.progress-success .bar,
-.progress .bar-success {
- background-color: #5eb95e;
- background-image: -moz-linear-gradient(top, #62c462, #57a957);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
- background-image: -webkit-linear-gradient(top, #62c462, #57a957);
- background-image: -o-linear-gradient(top, #62c462, #57a957);
- background-image: linear-gradient(to bottom, #62c462, #57a957);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
+@media (max-width: 767px) {
+ .visible-md.visible-xs {
+ display: block !important;
+ }
+ tr.visible-md.visible-xs {
+ display: table-row !important;
+ }
+ th.visible-md.visible-xs,
+ td.visible-md.visible-xs {
+ display: table-cell !important;
+ }
}
-.progress-success.progress-striped .bar,
-.progress-striped .bar-success {
- background-color: #62c462;
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+@media (min-width: 768px) and (max-width: 991px) {
+ .visible-md.visible-sm {
+ display: block !important;
+ }
+ tr.visible-md.visible-sm {
+ display: table-row !important;
+ }
+ th.visible-md.visible-sm,
+ td.visible-md.visible-sm {
+ display: table-cell !important;
+ }
}
-.progress-info .bar,
-.progress .bar-info {
- background-color: #4bb1cf;
- background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
- background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
- background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
- background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
+@media (min-width: 992px) and (max-width: 1199px) {
+ .visible-md {
+ display: block !important;
+ }
+ tr.visible-md {
+ display: table-row !important;
+ }
+ th.visible-md,
+ td.visible-md {
+ display: table-cell !important;
+ }
}
-.progress-info.progress-striped .bar,
-.progress-striped .bar-info {
- background-color: #5bc0de;
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+@media (min-width: 1200px) {
+ .visible-md.visible-lg {
+ display: block !important;
+ }
+ tr.visible-md.visible-lg {
+ display: table-row !important;
+ }
+ th.visible-md.visible-lg,
+ td.visible-md.visible-lg {
+ display: table-cell !important;
+ }
}
-.progress-warning .bar,
-.progress .bar-warning {
- background-color: #faa732;
- background-image: -moz-linear-gradient(top, #fbb450, #f89406);
- background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
- background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
- background-image: -o-linear-gradient(top, #fbb450, #f89406);
- background-image: linear-gradient(to bottom, #fbb450, #f89406);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
+.visible-lg {
+ display: none !important;
}
-.progress-warning.progress-striped .bar,
-.progress-striped .bar-warning {
- background-color: #fbb450;
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+tr.visible-lg {
+ display: none !important;
}
-.accordion {
- margin-bottom: 20px;
+th.visible-lg,
+td.visible-lg {
+ display: none !important;
}
-.accordion-group {
- margin-bottom: 2px;
- border: 1px solid #e5e5e5;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
+@media (max-width: 767px) {
+ .visible-lg.visible-xs {
+ display: block !important;
+ }
+ tr.visible-lg.visible-xs {
+ display: table-row !important;
+ }
+ th.visible-lg.visible-xs,
+ td.visible-lg.visible-xs {
+ display: table-cell !important;
+ }
}
-.accordion-heading {
- border-bottom: 0;
+@media (min-width: 768px) and (max-width: 991px) {
+ .visible-lg.visible-sm {
+ display: block !important;
+ }
+ tr.visible-lg.visible-sm {
+ display: table-row !important;
+ }
+ th.visible-lg.visible-sm,
+ td.visible-lg.visible-sm {
+ display: table-cell !important;
+ }
}
-.accordion-heading .accordion-toggle {
- display: block;
- padding: 8px 15px;
+@media (min-width: 992px) and (max-width: 1199px) {
+ .visible-lg.visible-md {
+ display: block !important;
+ }
+ tr.visible-lg.visible-md {
+ display: table-row !important;
+ }
+ th.visible-lg.visible-md,
+ td.visible-lg.visible-md {
+ display: table-cell !important;
+ }
}
-.accordion-toggle {
- cursor: pointer;
+@media (min-width: 1200px) {
+ .visible-lg {
+ display: block !important;
+ }
+ tr.visible-lg {
+ display: table-row !important;
+ }
+ th.visible-lg,
+ td.visible-lg {
+ display: table-cell !important;
+ }
}
-.accordion-inner {
- padding: 9px 15px;
- border-top: 1px solid #e5e5e5;
+.hidden-xs {
+ display: block !important;
}
-.carousel {
- position: relative;
- margin-bottom: 20px;
- line-height: 1;
+tr.hidden-xs {
+ display: table-row !important;
}
-.carousel-inner {
- position: relative;
- width: 100%;
- overflow: hidden;
+th.hidden-xs,
+td.hidden-xs {
+ display: table-cell !important;
}
-.carousel-inner > .item {
- position: relative;
- display: none;
- -webkit-transition: 0.6s ease-in-out left;
- -moz-transition: 0.6s ease-in-out left;
- -o-transition: 0.6s ease-in-out left;
- transition: 0.6s ease-in-out left;
+@media (max-width: 767px) {
+ .hidden-xs {
+ display: none !important;
+ }
+ tr.hidden-xs {
+ display: none !important;
+ }
+ th.hidden-xs,
+ td.hidden-xs {
+ display: none !important;
+ }
}
-.carousel-inner > .item > img,
-.carousel-inner > .item > a > img {
- display: block;
- line-height: 1;
+@media (min-width: 768px) and (max-width: 991px) {
+ .hidden-xs.hidden-sm {
+ display: none !important;
+ }
+ tr.hidden-xs.hidden-sm {
+ display: none !important;
+ }
+ th.hidden-xs.hidden-sm,
+ td.hidden-xs.hidden-sm {
+ display: none !important;
+ }
}
-.carousel-inner > .active,
-.carousel-inner > .next,
-.carousel-inner > .prev {
- display: block;
+@media (min-width: 992px) and (max-width: 1199px) {
+ .hidden-xs.hidden-md {
+ display: none !important;
+ }
+ tr.hidden-xs.hidden-md {
+ display: none !important;
+ }
+ th.hidden-xs.hidden-md,
+ td.hidden-xs.hidden-md {
+ display: none !important;
+ }
}
-.carousel-inner > .active {
- left: 0;
+@media (min-width: 1200px) {
+ .hidden-xs.hidden-lg {
+ display: none !important;
+ }
+ tr.hidden-xs.hidden-lg {
+ display: none !important;
+ }
+ th.hidden-xs.hidden-lg,
+ td.hidden-xs.hidden-lg {
+ display: none !important;
+ }
}
-.carousel-inner > .next,
-.carousel-inner > .prev {
- position: absolute;
- top: 0;
- width: 100%;
+.hidden-sm {
+ display: block !important;
}
-.carousel-inner > .next {
- left: 100%;
+tr.hidden-sm {
+ display: table-row !important;
}
-.carousel-inner > .prev {
- left: -100%;
+th.hidden-sm,
+td.hidden-sm {
+ display: table-cell !important;
}
-.carousel-inner > .next.left,
-.carousel-inner > .prev.right {
- left: 0;
+@media (max-width: 767px) {
+ .hidden-sm.hidden-xs {
+ display: none !important;
+ }
+ tr.hidden-sm.hidden-xs {
+ display: none !important;
+ }
+ th.hidden-sm.hidden-xs,
+ td.hidden-sm.hidden-xs {
+ display: none !important;
+ }
}
-.carousel-inner > .active.left {
- left: -100%;
+@media (min-width: 768px) and (max-width: 991px) {
+ .hidden-sm {
+ display: none !important;
+ }
+ tr.hidden-sm {
+ display: none !important;
+ }
+ th.hidden-sm,
+ td.hidden-sm {
+ display: none !important;
+ }
}
-.carousel-inner > .active.right {
- left: 100%;
+@media (min-width: 992px) and (max-width: 1199px) {
+ .hidden-sm.hidden-md {
+ display: none !important;
+ }
+ tr.hidden-sm.hidden-md {
+ display: none !important;
+ }
+ th.hidden-sm.hidden-md,
+ td.hidden-sm.hidden-md {
+ display: none !important;
+ }
}
-.carousel-control {
- position: absolute;
- top: 40%;
- left: 15px;
- width: 40px;
- height: 40px;
- margin-top: -20px;
- font-size: 60px;
- font-weight: 100;
- line-height: 30px;
- color: #ffffff;
- text-align: center;
- background: #222222;
- border: 3px solid #ffffff;
- -webkit-border-radius: 23px;
- -moz-border-radius: 23px;
- border-radius: 23px;
- opacity: 0.5;
- filter: alpha(opacity=50);
+@media (min-width: 1200px) {
+ .hidden-sm.hidden-lg {
+ display: none !important;
+ }
+ tr.hidden-sm.hidden-lg {
+ display: none !important;
+ }
+ th.hidden-sm.hidden-lg,
+ td.hidden-sm.hidden-lg {
+ display: none !important;
+ }
}
-.carousel-control.right {
- right: 15px;
- left: auto;
+.hidden-md {
+ display: block !important;
}
-.carousel-control:hover,
-.carousel-control:focus {
- color: #ffffff;
- text-decoration: none;
- opacity: 0.9;
- filter: alpha(opacity=90);
+tr.hidden-md {
+ display: table-row !important;
}
-.carousel-indicators {
- position: absolute;
- top: 15px;
- right: 15px;
- z-index: 5;
- margin: 0;
- list-style: none;
+th.hidden-md,
+td.hidden-md {
+ display: table-cell !important;
}
-.carousel-indicators li {
- display: block;
- float: left;
- width: 10px;
- height: 10px;
- margin-left: 5px;
- text-indent: -999px;
- background-color: #ccc;
- background-color: rgba(255, 255, 255, 0.25);
- border-radius: 5px;
+@media (max-width: 767px) {
+ .hidden-md.hidden-xs {
+ display: none !important;
+ }
+ tr.hidden-md.hidden-xs {
+ display: none !important;
+ }
+ th.hidden-md.hidden-xs,
+ td.hidden-md.hidden-xs {
+ display: none !important;
+ }
}
-.carousel-indicators .active {
- background-color: #fff;
+@media (min-width: 768px) and (max-width: 991px) {
+ .hidden-md.hidden-sm {
+ display: none !important;
+ }
+ tr.hidden-md.hidden-sm {
+ display: none !important;
+ }
+ th.hidden-md.hidden-sm,
+ td.hidden-md.hidden-sm {
+ display: none !important;
+ }
}
-.carousel-caption {
- position: absolute;
- right: 0;
- bottom: 0;
- left: 0;
- padding: 15px;
- background: #333333;
- background: rgba(0, 0, 0, 0.75);
+@media (min-width: 992px) and (max-width: 1199px) {
+ .hidden-md {
+ display: none !important;
+ }
+ tr.hidden-md {
+ display: none !important;
+ }
+ th.hidden-md,
+ td.hidden-md {
+ display: none !important;
+ }
}
-.carousel-caption h4,
-.carousel-caption p {
- line-height: 20px;
- color: #ffffff;
+@media (min-width: 1200px) {
+ .hidden-md.hidden-lg {
+ display: none !important;
+ }
+ tr.hidden-md.hidden-lg {
+ display: none !important;
+ }
+ th.hidden-md.hidden-lg,
+ td.hidden-md.hidden-lg {
+ display: none !important;
+ }
}
-.carousel-caption h4 {
- margin: 0 0 5px;
+.hidden-lg {
+ display: block !important;
}
-.carousel-caption p {
- margin-bottom: 0;
+tr.hidden-lg {
+ display: table-row !important;
}
-.hero-unit {
- padding: 60px;
- margin-bottom: 30px;
- font-size: 18px;
- font-weight: 200;
- line-height: 30px;
- color: inherit;
- background-color: #eeeeee;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
+th.hidden-lg,
+td.hidden-lg {
+ display: table-cell !important;
}
-.hero-unit h1 {
- margin-bottom: 0;
- font-size: 60px;
- line-height: 1;
- letter-spacing: -1px;
- color: inherit;
+@media (max-width: 767px) {
+ .hidden-lg.hidden-xs {
+ display: none !important;
+ }
+ tr.hidden-lg.hidden-xs {
+ display: none !important;
+ }
+ th.hidden-lg.hidden-xs,
+ td.hidden-lg.hidden-xs {
+ display: none !important;
+ }
}
-.hero-unit li {
- line-height: 30px;
+@media (min-width: 768px) and (max-width: 991px) {
+ .hidden-lg.hidden-sm {
+ display: none !important;
+ }
+ tr.hidden-lg.hidden-sm {
+ display: none !important;
+ }
+ th.hidden-lg.hidden-sm,
+ td.hidden-lg.hidden-sm {
+ display: none !important;
+ }
}
-.pull-right {
- float: right;
+@media (min-width: 992px) and (max-width: 1199px) {
+ .hidden-lg.hidden-md {
+ display: none !important;
+ }
+ tr.hidden-lg.hidden-md {
+ display: none !important;
+ }
+ th.hidden-lg.hidden-md,
+ td.hidden-lg.hidden-md {
+ display: none !important;
+ }
}
-.pull-left {
- float: left;
+@media (min-width: 1200px) {
+ .hidden-lg {
+ display: none !important;
+ }
+ tr.hidden-lg {
+ display: none !important;
+ }
+ th.hidden-lg,
+ td.hidden-lg {
+ display: none !important;
+ }
}
-.hide {
- display: none;
+.visible-print {
+ display: none !important;
}
-.show {
- display: block;
+tr.visible-print {
+ display: none !important;
}
-.invisible {
- visibility: hidden;
+th.visible-print,
+td.visible-print {
+ display: none !important;
}
-.affix {
- position: fixed;
+@media print {
+ .visible-print {
+ display: block !important;
+ }
+ tr.visible-print {
+ display: table-row !important;
+ }
+ th.visible-print,
+ td.visible-print {
+ display: table-cell !important;
+ }
+ .hidden-print {
+ display: none !important;
+ }
+ tr.hidden-print {
+ display: none !important;
+ }
+ th.hidden-print,
+ td.hidden-print {
+ display: none !important;
+ }
}
diff --git a/framework/yii/requirements/views/web/index.php b/framework/yii/requirements/views/web/index.php
index 1887f8b..287d4bb 100644
--- a/framework/yii/requirements/views/web/index.php
+++ b/framework/yii/requirements/views/web/index.php
@@ -1,7 +1,7 @@
@@ -52,18 +52,18 @@
Name Result Required By Memo
-
+
-
+
-
+
-
+
-
+
@@ -74,7 +74,7 @@
diff --git a/framework/yii/test/DbFixtureManager.php b/framework/yii/test/DbFixtureManager.php
new file mode 100644
index 0000000..d78d28f
--- /dev/null
+++ b/framework/yii/test/DbFixtureManager.php
@@ -0,0 +1,220 @@
+
+ * @since 2.0
+ */
+class DbFixtureManager extends Component
+{
+ /**
+ * @var string the init script file that should be executed before running each test.
+ * This should be a path relative to [[basePath]].
+ */
+ public $initScript = 'init.php';
+ /**
+ * @var string the base path containing all fixtures. This can be either a directory path or path alias.
+ */
+ public $basePath = '@app/tests/fixtures';
+ /**
+ * @var Connection|string the DB connection object or the application component ID of the DB connection.
+ * After the DbFixtureManager object is created, if you want to change this property, you should only assign it
+ * with a DB connection object.
+ */
+ public $db = 'db';
+ /**
+ * @var array list of database schemas that the test tables may reside in. Defaults to
+ * [''], meaning using the default schema (an empty string refers to the
+ * default schema). This property is mainly used when turning on and off integrity checks
+ * so that fixture data can be populated into the database without causing problem.
+ */
+ public $schemas = [''];
+
+ private $_rows; // fixture name, row alias => row
+ private $_models; // fixture name, row alias => record (or class name)
+ private $_modelClasses;
+
+
+ /**
+ * Loads the specified fixtures.
+ *
+ * This method does the following things to load the fixtures:
+ *
+ * - Run [[initScript]] if any.
+ * - Clean up data and models loaded in memory previously.
+ * - Load each specified fixture by calling [[loadFixture()]].
+ *
+ * @param array $fixtures a list of fixtures (fixture name => table name or AR class name) to be loaded.
+ * Each array element can be either a table name (with schema prefix if needed), or a fully-qualified
+ * ActiveRecord class name (e.g. `app\models\Post`). An element can be associated with a key
+ * which will be treated as the fixture name.
+ * @return array the loaded fixture data (fixture name => table rows)
+ * @throws InvalidConfigException if a model class specifying a fixture is not an ActiveRecord class.
+ */
+ public function load(array $fixtures = [])
+ {
+ $this->basePath = Yii::getAlias($this->basePath);
+
+ if (is_string($this->db)) {
+ $this->db = Yii::$app->getComponent($this->db);
+ }
+ if (!$this->db instanceof Connection) {
+ throw new InvalidConfigException("The 'db' property must be either a DB connection instance or the application component ID of a DB connection.");
+ }
+
+ foreach ($fixtures as $name => $fixture) {
+ if (strpos($fixture, '\\') !== false) {
+ $model = new $fixture;
+ if ($model instanceof ActiveRecordInterface) {
+ $this->_modelClasses[$name] = $fixture;
+ $fixtures[$name] = $model->getTableSchema()->name;
+ } else {
+ throw new InvalidConfigException("Fixture '$fixture' must be an ActiveRecord class.");
+ }
+ }
+ }
+
+ $this->_modelClasses = $this->_rows = $this->_models = [];
+
+ $this->checkIntegrity(false);
+
+ if (!empty($this->initScript)) {
+ $initFile = $this->basePath . '/' . $this->initScript;
+ if (is_file($initFile)) {
+ require($initFile);
+ }
+ }
+
+ foreach ($fixtures as $name => $tableName) {
+ $rows = $this->loadFixture($tableName);
+ if (is_array($rows)) {
+ $this->_rows[$name] = $rows;
+ }
+ }
+ $this->checkIntegrity(true);
+ return $this->_rows;
+ }
+
+ /**
+ * Loads the fixture for the specified table.
+ *
+ * This method does the following tasks to load the fixture for a table:
+ *
+ * - Remove existing rows in the table.
+ * - If there is any auto-incremental column, the corresponding sequence will be reset to 0.
+ * - If a fixture file is found, it will be executed, and its return value will be treated
+ * as rows which will then be inserted into the table.
+ *
+ * @param string $tableName table name
+ * @return array|boolean the loaded fixture rows indexed by row aliases (if any).
+ * False is returned if the table does not have a fixture.
+ * @throws InvalidConfigException if the specified table does not exist
+ */
+ public function loadFixture($tableName)
+ {
+ $table = $this->db->getSchema()->getTableSchema($tableName);
+ if ($table === null) {
+ throw new InvalidConfigException("Table does not exist: $tableName");
+ }
+
+ $this->db->createCommand()->truncateTable($tableName)->execute();
+
+ $fileName = $this->basePath . '/' . $tableName . '.php';
+ if (!is_file($fileName)) {
+ return false;
+ }
+
+ $rows = [];
+ foreach (require($fileName) as $alias => $row) {
+ $this->db->createCommand()->insert($tableName, $row)->execute();
+ if ($table->sequenceName !== null) {
+ foreach ($table->primaryKey as $pk) {
+ if (!isset($row[$pk])) {
+ $row[$pk] = $this->db->getLastInsertID($table->sequenceName);
+ break;
+ }
+ }
+ }
+ $rows[$alias] = $row;
+ }
+
+ return $rows;
+ }
+
+ /**
+ * Returns the fixture data rows.
+ * The rows will have updated primary key values if the primary key is auto-incremental.
+ * @param string $fixtureName the fixture name
+ * @return array the fixture data rows. False is returned if there is no such fixture data.
+ */
+ public function getRows($fixtureName)
+ {
+ return isset($this->_rows[$fixtureName]) ? $this->_rows[$fixtureName] : false;
+ }
+
+ /**
+ * Returns the specified ActiveRecord instance in the fixture data.
+ * @param string $fixtureName the fixture name
+ * @param string $modelName the alias for the fixture data row
+ * @return \yii\db\ActiveRecord the ActiveRecord instance. Null is returned if there is no such fixture row.
+ */
+ public function getModel($fixtureName, $modelName)
+ {
+ if (!isset($this->_modelClasses[$fixtureName]) || !isset($this->_rows[$fixtureName][$modelName])) {
+ return null;
+ }
+ if (isset($this->_models[$fixtureName][$modelName])) {
+ return $this->_models[$fixtureName][$modelName];
+ }
+ $row = $this->_rows[$fixtureName][$modelName];
+ /** @var \yii\db\ActiveRecord $modelClass */
+ $modelClass = $this->_models[$fixtureName];
+ /** @var \yii\db\ActiveRecord $model */
+ $model = new $modelClass;
+ $keys = [];
+ foreach ($model->primaryKey() as $key) {
+ $keys[$key] = isset($row[$key]) ? $row[$key] : null;
+ }
+ return $this->_models[$fixtureName][$modelName] = $modelClass::find($keys);
+ }
+
+ /**
+ * Enables or disables database integrity check.
+ * This method may be used to temporarily turn off foreign constraints check.
+ * @param boolean $check whether to enable database integrity check
+ */
+ public function checkIntegrity($check)
+ {
+ foreach ($this->schemas as $schema) {
+ $this->db->createCommand()->checkIntegrity($check, $schema)->execute();
+ }
+ }
+}
diff --git a/framework/yii/test/DbTestTrait.php b/framework/yii/test/DbTestTrait.php
new file mode 100644
index 0000000..8a6dc3c
--- /dev/null
+++ b/framework/yii/test/DbTestTrait.php
@@ -0,0 +1,110 @@
+loadFixtures([
+ * 'posts' => Post::className(),
+ * 'users' => User::className(),
+ * ]);
+ * }
+ * }
+ * ~~~
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+trait DbTestTrait
+{
+ /**
+ * Loads the specified fixtures.
+ *
+ * This method should typically be called in the setup method of test cases so that
+ * the fixtures are loaded before running each test method.
+ *
+ * This method does the following things:
+ *
+ * - Run [[DbFixtureManager::initScript]] if it is found under [[DbFixtureManager::basePath]].
+ * - Clean up data and models loaded in memory previously.
+ * - Load each specified fixture:
+ * * Truncate the corresponding table.
+ * * If a fixture file named `TableName.php` is found under [[DbFixtureManager::basePath]],
+ * the file will be executed, and the return value will be treated as rows which will
+ * then be inserted into the table.
+ *
+ * @param array $fixtures a list of fixtures (fixture name => table name or AR class name) to be loaded.
+ * Each array element can be either a table name (with schema prefix if needed), or a fully-qualified
+ * ActiveRecord class name (e.g. `app\models\Post`). An element can be optionally associated with a key
+ * which will be treated as the fixture name. For example,
+ *
+ * ~~~
+ * [
+ * 'tbl_comment',
+ * 'users' => 'tbl_user', // 'users' is the fixture name, 'tbl_user' is a table name
+ * 'posts' => 'app\models\Post, // 'app\models\Post' is a model class name
+ * ]
+ * ~~~
+ *
+ * @return array the loaded fixture data (fixture name => table rows)
+ */
+ public function loadFixtures(array $fixtures = [])
+ {
+ return $this->getFixtureManager()->load($fixtures);
+ }
+
+ /**
+ * Returns the DB fixture manager.
+ * @return DbFixtureManager the DB fixture manager
+ */
+ public function getFixtureManager()
+ {
+ return Yii::$app->getComponent('fixture');
+ }
+
+ /**
+ * Returns the table rows of the named fixture.
+ * @param string $fixtureName the fixture name.
+ * @return array the named fixture table rows. False is returned if there is no such fixture data.
+ */
+ public function getFixtureRows($fixtureName)
+ {
+ return $this->getFixtureManager()->getRows($fixtureName);
+ }
+
+ /**
+ * Returns the named AR instance corresponding to the named fixture.
+ * @param string $fixtureName the fixture name.
+ * @param string $modelName the name of the fixture data row
+ * @return \yii\db\ActiveRecord the named AR instance corresponding to the named fixture.
+ * Null is returned if there is no such fixture or the record cannot be found.
+ */
+ public function getFixtureModel($fixtureName, $modelName)
+ {
+ return $this->getFixtureManager()->getModel($fixtureName, $modelName);
+ }
+}
diff --git a/framework/yii/test/TestCase.php b/framework/yii/test/TestCase.php
deleted file mode 100644
index 2e9b480..0000000
--- a/framework/yii/test/TestCase.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- * @since 2.0
- */
-abstract class TestCase extends \PHPUnit_Framework_TestCase
-{
-}
diff --git a/framework/yii/test/WebTestCase.php b/framework/yii/test/WebTestCase.php
deleted file mode 100644
index 39162c9..0000000
--- a/framework/yii/test/WebTestCase.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- * @since 2.0
- */
-abstract class WebTestCase extends \PHPUnit_Extensions_SeleniumTestCase
-{
-}
diff --git a/framework/yii/validators/BooleanValidator.php b/framework/yii/validators/BooleanValidator.php
index 67a64f6..8bca827 100644
--- a/framework/yii/validators/BooleanValidator.php
+++ b/framework/yii/validators/BooleanValidator.php
@@ -37,7 +37,7 @@ class BooleanValidator extends Validator
public $strict = false;
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
@@ -48,53 +48,36 @@ class BooleanValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
- public function validateAttribute($object, $attribute)
+ protected function validateValue($value)
{
- $value = $object->$attribute;
- if (!$this->validateValue($value)) {
- $this->addError($object, $attribute, $this->message, array(
- '{true}' => $this->trueValue,
- '{false}' => $this->falseValue,
- ));
- }
- }
-
- /**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
- */
- public function validateValue($value)
- {
- return !$this->strict && ($value == $this->trueValue || $value == $this->falseValue)
+ $valid = !$this->strict && ($value == $this->trueValue || $value == $this->falseValue)
|| $this->strict && ($value === $this->trueValue || $value === $this->falseValue);
+ if (!$valid) {
+ return [$this->message, [
+ 'true' => $this->trueValue,
+ 'false' => $this->falseValue,
+ ]];
+ } else {
+ return null;
+ }
}
/**
- * Returns the JavaScript needed for performing client-side validation.
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @return string the client-side validation script.
+ * @inheritdoc
*/
public function clientValidateAttribute($object, $attribute, $view)
{
- $options = array(
+ $options = [
'trueValue' => $this->trueValue,
'falseValue' => $this->falseValue,
- 'message' => Html::encode(strtr($this->message, array(
+ 'message' => strtr($this->message, [
'{attribute}' => $object->getAttributeLabel($attribute),
- '{value}' => $object->$attribute,
'{true}' => $this->trueValue,
'{false}' => $this->falseValue,
- ))),
- );
+ ]),
+ ];
if ($this->skipOnEmpty) {
$options['skipOnEmpty'] = 1;
}
@@ -102,7 +85,7 @@ class BooleanValidator extends Validator
$options['strict'] = 1;
}
- $view->registerAssetBundle('yii/validation');
+ ValidationAsset::register($view);
return 'yii.validation.boolean(value, messages, ' . json_encode($options) . ');';
}
}
diff --git a/framework/yii/validators/CaptchaValidator.php b/framework/yii/validators/CaptchaValidator.php
deleted file mode 100644
index 01870d3..0000000
--- a/framework/yii/validators/CaptchaValidator.php
+++ /dev/null
@@ -1,119 +0,0 @@
-
- * @since 2.0
- */
-class CaptchaValidator extends Validator
-{
- public $skipOnEmpty = false;
- /**
- * @var boolean whether the comparison is case sensitive. Defaults to false.
- */
- public $caseSensitive = false;
- /**
- * @var string the route of the controller action that renders the CAPTCHA image.
- */
- public $captchaAction = 'site/captcha';
-
-
- /**
- * Initializes the validator.
- */
- public function init()
- {
- parent::init();
- if ($this->message === null) {
- $this->message = Yii::t('yii', 'The verification code is incorrect.');
- }
- }
-
- /**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
- */
- public function validateAttribute($object, $attribute)
- {
- $value = $object->$attribute;
- if (!$this->validateValue($value)) {
- $this->addError($object, $attribute, $this->message);
- }
- }
-
- /**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
- */
- public function validateValue($value)
- {
- $captcha = $this->getCaptchaAction();
- return !is_array($value) && $captcha->validate($value, $this->caseSensitive);
- }
-
- /**
- * Returns the CAPTCHA action object.
- * @throws InvalidConfigException
- * @return \yii\web\CaptchaAction the action object
- */
- public function getCaptchaAction()
- {
- $ca = Yii::$app->createController($this->captchaAction);
- if ($ca !== false) {
- /** @var \yii\base\Controller $controller */
- list($controller, $actionID) = $ca;
- $action = $controller->createAction($actionID);
- if ($action !== null) {
- return $action;
- }
- }
- throw new InvalidConfigException('Invalid CAPTCHA action ID: ' . $this->captchaAction);
- }
-
- /**
- * Returns the JavaScript needed for performing client-side validation.
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @return string the client-side validation script.
- */
- public function clientValidateAttribute($object, $attribute, $view)
- {
- $captcha = $this->getCaptchaAction();
- $code = $captcha->getVerifyCode(false);
- $hash = $captcha->generateValidationHash($this->caseSensitive ? $code : strtolower($code));
- $options = array(
- 'hash' => $hash,
- 'hashKey' => 'yiiCaptcha/' . $this->captchaAction,
- 'caseSensitive' => $this->caseSensitive,
- 'message' => Html::encode(strtr($this->message, array(
- '{attribute}' => $object->getAttributeLabel($attribute),
- '{value}' => $object->$attribute,
- ))),
- );
- if ($this->skipOnEmpty) {
- $options['skipOnEmpty'] = 1;
- }
-
- $view->registerAssetBundle('yii/validation');
- return 'yii.validation.captcha(value, messages, ' . json_encode($options) . ');';
- }
-}
diff --git a/framework/yii/validators/CompareValidator.php b/framework/yii/validators/CompareValidator.php
index c94577c..cbd12d2 100644
--- a/framework/yii/validators/CompareValidator.php
+++ b/framework/yii/validators/CompareValidator.php
@@ -41,7 +41,7 @@ class CompareValidator extends Validator
*/
public $compareAttribute;
/**
- * @var string the constant value to be compared with. When both this property
+ * @var mixed the constant value to be compared with. When both this property
* and [[compareAttribute]] are set, this property takes precedence.
* @see compareAttribute
*/
@@ -66,12 +66,13 @@ class CompareValidator extends Validator
* - `{attribute}`: the label of the attribute being validated
* - `{value}`: the value of the attribute being validated
* - `{compareValue}`: the value or the attribute label to be compared with
+ * - `{compareAttribute}`: the label of the attribute to be compared with
*/
public $message;
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
@@ -109,11 +110,7 @@ class CompareValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
- * @throws InvalidConfigException if CompareValidator::operator is invalid
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
@@ -130,61 +127,60 @@ class CompareValidator extends Validator
$compareLabel = $object->getAttributeLabel($compareAttribute);
}
- switch ($this->operator) {
- case '==': $valid = $value == $compareValue; break;
- case '===': $valid = $value === $compareValue; break;
- case '!=': $valid = $value != $compareValue; break;
- case '!==': $valid = $value !== $compareValue; break;
- case '>': $valid = $value > $compareValue; break;
- case '>=': $valid = $value >= $compareValue; break;
- case '<': $valid = $value < $compareValue; break;
- case '<=': $valid = $value <= $compareValue; break;
- default: $valid = false; break;
- }
- if (!$valid) {
- $this->addError($object, $attribute, $this->message, array(
- '{compareAttribute}' => $compareLabel,
- '{compareValue}' => $compareValue,
- ));
+ if (!$this->compareValues($this->operator, $value, $compareValue)) {
+ $this->addError($object, $attribute, $this->message, [
+ 'compareAttribute' => $compareLabel,
+ 'compareValue' => $compareValue,
+ ]);
}
}
/**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
- * @throws InvalidConfigException if [[compareValue]] is not set.
+ * @inheritdoc
*/
- public function validateValue($value)
+ protected function validateValue($value)
{
if ($this->compareValue === null) {
throw new InvalidConfigException('CompareValidator::compareValue must be set.');
}
+ if (!$this->compareValues($this->operator, $value, $this->compareValue)) {
+ return [$this->message, [
+ 'compareAttribute' => $this->compareValue,
+ 'compareValue' => $this->compareValue,
+ ]];
+ } else {
+ return null;
+ }
+ }
- switch ($this->operator) {
- case '==': return $value == $this->compareValue;
- case '===': return $value === $this->compareValue;
- case '!=': return $value != $this->compareValue;
- case '!==': return $value !== $this->compareValue;
- case '>': return $value > $this->compareValue;
- case '>=': return $value >= $this->compareValue;
- case '<': return $value < $this->compareValue;
- case '<=': return $value <= $this->compareValue;
+ /**
+ * Compares two values with the specified operator.
+ * @param string $operator the comparison operator
+ * @param mixed $value the value being compared
+ * @param mixed $compareValue another value being compared
+ * @return boolean whether the comparison using the specified operator is true.
+ */
+ protected function compareValues($operator, $value, $compareValue)
+ {
+ switch ($operator) {
+ case '==': return $value == $compareValue;
+ case '===': return $value === $compareValue;
+ case '!=': return $value != $compareValue;
+ case '!==': return $value !== $compareValue;
+ case '>': return $value > $compareValue;
+ case '>=': return $value >= $compareValue;
+ case '<': return $value < $compareValue;
+ case '<=': return $value <= $compareValue;
+ default: return false;
}
}
/**
- * Returns the JavaScript needed for performing client-side validation.
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated
- * @return string the client-side validation script
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @throws InvalidConfigException if CompareValidator::operator is invalid
+ * @inheritdoc
*/
public function clientValidateAttribute($object, $attribute, $view)
{
- $options = array('operator' => $this->operator);
+ $options = ['operator' => $this->operator];
if ($this->compareValue !== null) {
$options['compareValue'] = $this->compareValue;
@@ -199,13 +195,13 @@ class CompareValidator extends Validator
$options['skipOnEmpty'] = 1;
}
- $options['message'] = Html::encode(strtr($this->message, array(
+ $options['message'] = strtr($this->message, [
'{attribute}' => $object->getAttributeLabel($attribute),
- '{value}' => $object->$attribute,
+ '{compareAttribute}' => $compareValue,
'{compareValue}' => $compareValue,
- )));
+ ]);
- $view->registerAssetBundle('yii/validation');
+ ValidationAsset::register($view);
return 'yii.validation.compare(value, messages, ' . json_encode($options) . ');';
}
}
diff --git a/framework/yii/validators/DateValidator.php b/framework/yii/validators/DateValidator.php
index 2f9e18b..f5544a0 100644
--- a/framework/yii/validators/DateValidator.php
+++ b/framework/yii/validators/DateValidator.php
@@ -32,7 +32,7 @@ class DateValidator extends Validator
public $timestampAttribute;
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
@@ -43,33 +43,31 @@ class DateValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
$value = $object->$attribute;
- if (is_array($value)) {
- $this->addError($object, $attribute, $this->message);
- return;
- }
- $date = DateTime::createFromFormat($this->format, $value);
- if ($date === false) {
- $this->addError($object, $attribute, $this->message);
+ $result = $this->validateValue($value);
+ if (!empty($result)) {
+ $this->addError($object, $attribute, $result[0], $result[1]);
} elseif ($this->timestampAttribute !== null) {
+ $date = DateTime::createFromFormat($this->format, $value);
$object->{$this->timestampAttribute} = $date->getTimestamp();
}
}
/**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
+ * @inheritdoc
*/
- public function validateValue($value)
+ protected function validateValue($value)
{
- return DateTime::createFromFormat($this->format, $value) !== false;
+ if (is_array($value)) {
+ return [$this->message, []];
+ }
+ $date = DateTime::createFromFormat($this->format, $value);
+ $errors = DateTime::getLastErrors();
+ $invalid = $date === false || $errors['error_count'] || $errors['warning_count'];
+ return $invalid ? [$this->message, []] : null;
}
}
diff --git a/framework/yii/validators/DefaultValueValidator.php b/framework/yii/validators/DefaultValueValidator.php
index 20df5fd..0db3a67 100644
--- a/framework/yii/validators/DefaultValueValidator.php
+++ b/framework/yii/validators/DefaultValueValidator.php
@@ -29,9 +29,7 @@ class DefaultValueValidator extends Validator
public $skipOnEmpty = false;
/**
- * Validates the attribute of the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
diff --git a/framework/yii/validators/EmailValidator.php b/framework/yii/validators/EmailValidator.php
index 292da88..e5d9b75 100644
--- a/framework/yii/validators/EmailValidator.php
+++ b/framework/yii/validators/EmailValidator.php
@@ -38,16 +38,11 @@ class EmailValidator extends Validator
*/
public $allowName = false;
/**
- * @var boolean whether to check the MX record for the email address.
- * Defaults to false. To enable it, you need to make sure the PHP function 'checkdnsrr'
- * exists in your PHP installation.
+ * @var boolean whether to check whether the emails domain exists and has either an A or MX record.
+ * Be aware of the fact that this check can fail due to temporary DNS problems even if the email address is
+ * valid and an email would be deliverable. Defaults to false.
*/
- public $checkMX = false;
- /**
- * @var boolean whether to check port 25 for the email address.
- * Defaults to false.
- */
- public $checkPort = false;
+ public $checkDNS = false;
/**
* @var boolean whether validation process should take into account IDN (internationalized domain
* names). Defaults to false meaning that validation of emails containing IDN will always fail.
@@ -58,7 +53,7 @@ class EmailValidator extends Validator
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
@@ -72,76 +67,49 @@ class EmailValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
- */
- public function validateAttribute($object, $attribute)
- {
- $value = $object->$attribute;
- if (!$this->validateValue($value)) {
- $this->addError($object, $attribute, $this->message);
- }
- }
-
- /**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
+ * @inheritdoc
*/
- public function validateValue($value)
+ protected function validateValue($value)
{
// make sure string length is limited to avoid DOS attacks
- if (!is_string($value) || strlen($value) >= 255) {
- return false;
- }
- if (($atPosition = strpos($value, '@')) === false) {
- return false;
- }
- $domain = rtrim(substr($value, $atPosition + 1), '>');
- if ($this->enableIDN) {
- $value = idn_to_ascii(ltrim(substr($value, 0, $atPosition), '<')) . '@' . idn_to_ascii($domain);
- }
- $valid = preg_match($this->pattern, $value) || $this->allowName && preg_match($this->fullPattern, $value);
- if ($valid) {
- if ($this->checkMX && function_exists('checkdnsrr')) {
- $valid = checkdnsrr($domain, 'MX');
+ if (!is_string($value) || strlen($value) >= 320) {
+ $valid = false;
+ } elseif (!preg_match('/^(.*)(.*)@(.*)(>?)$/', $value, $matches)) {
+ $valid = false;
+ } else {
+ $domain = $matches[3];
+ if ($this->enableIDN) {
+ $value = $matches[1] . idn_to_ascii($matches[2]) . '@' . idn_to_ascii($domain) . $matches[4];
}
- if ($valid && $this->checkPort && function_exists('fsockopen')) {
- $valid = fsockopen($domain, 25) !== false;
+ $valid = preg_match($this->pattern, $value) || $this->allowName && preg_match($this->fullPattern, $value);
+ if ($valid && $this->checkDNS) {
+ $valid = checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A');
}
}
- return $valid;
+ return $valid ? null : [$this->message, []];
}
/**
- * Returns the JavaScript needed for performing client-side validation.
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @return string the client-side validation script.
+ * @inheritdoc
*/
public function clientValidateAttribute($object, $attribute, $view)
{
- $options = array(
+ $options = [
'pattern' => new JsExpression($this->pattern),
'fullPattern' => new JsExpression($this->fullPattern),
'allowName' => $this->allowName,
- 'message' => Html::encode(strtr($this->message, array(
+ 'message' => strtr($this->message, [
'{attribute}' => $object->getAttributeLabel($attribute),
- '{value}' => $object->$attribute,
- ))),
+ ]),
'enableIDN' => (boolean)$this->enableIDN,
- );
+ ];
if ($this->skipOnEmpty) {
$options['skipOnEmpty'] = 1;
}
- $view->registerAssetBundle('yii/validation');
+ ValidationAsset::register($view);
if ($this->enableIDN) {
- $view->registerAssetBundle('yii/punycode');
+ PunycodeAsset::register($view);
}
return 'yii.validation.email(value, messages, ' . Json::encode($options) . ');';
}
diff --git a/framework/yii/validators/ExistValidator.php b/framework/yii/validators/ExistValidator.php
index 9c74890..caefc49 100644
--- a/framework/yii/validators/ExistValidator.php
+++ b/framework/yii/validators/ExistValidator.php
@@ -13,33 +13,51 @@ use yii\base\InvalidConfigException;
/**
* ExistValidator validates that the attribute value exists in a table.
*
+ * ExistValidator checks if the value being validated can be found in the table column specified by
+ * the ActiveRecord class [[targetClass]] and the attribute [[targetAttribute]].
+ *
* This validator is often used to verify that a foreign key contains a value
* that can be found in the foreign table.
*
+ * The followings are examples of validation rules using this validator:
+ *
+ * ```php
+ * // a1 needs to exist
+ * ['a1', 'exist']
+ * // a1 needs to exist, but its value will use a2 to check for the existence
+ * ['a1', 'exist', 'targetAttribute' => 'a2']
+ * // a1 and a2 need to exist together, and they both will receive error message
+ * [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']]
+ * // a1 and a2 need to exist together, only a1 will receive error message
+ * ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']]
+ * // a1 needs to exist by checking the existence of both a2 and a3 (using a1 value)
+ * ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']]
+ * ```
+ *
* @author Qiang Xue
* @since 2.0
*/
class ExistValidator extends Validator
{
/**
- * @var string the ActiveRecord class name or alias of the class
- * that should be used to look for the attribute value being validated.
- * Defaults to null, meaning using the ActiveRecord class of
- * the attribute being validated.
- * @see attributeName
+ * @var string the name of the ActiveRecord class that should be used to validate the existence
+ * of the current attribute value. It not set, it will use the ActiveRecord class of the attribute being validated.
+ * @see targetAttribute
*/
- public $className;
+ public $targetClass;
/**
- * @var string the yii\db\ActiveRecord class attribute name that should be
- * used to look for the attribute value being validated. Defaults to null,
- * meaning using the name of the attribute being validated.
- * @see className
+ * @var string|array the name of the ActiveRecord attribute that should be used to
+ * validate the existence of the current attribute value. If not set, it will use the name
+ * of the attribute currently being validated. You may use an array to validate the existence
+ * of multiple columns at the same time. The array values are the attributes that will be
+ * used to validate the existence, while the array keys are the attributes whose values are to be validated.
+ * If the key and the value are the same, you can just specify the value.
*/
- public $attributeName;
+ public $targetAttribute;
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
@@ -50,52 +68,55 @@ class ExistValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- *
- * @param \yii\db\ActiveRecord $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
- $value = $object->$attribute;
+ /** @var \yii\db\ActiveRecordInterface $targetClass */
+ $targetClass = $this->targetClass === null ? get_class($object) : $this->targetClass;
+ $targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;
- if (is_array($value)) {
- $this->addError($object, $attribute, $this->message);
- return;
+ if (is_array($targetAttribute)) {
+ $params = [];
+ foreach ($targetAttribute as $k => $v) {
+ $params[$v] = is_integer($k) ? $object->$v : $object->$k;
+ }
+ } else {
+ $params = [$targetAttribute => $object->$attribute];
+ }
+
+ foreach ($params as $value) {
+ if (is_array($value)) {
+ $this->addError($object, $attribute, Yii::t('yii', '{attribute} is invalid.'));
+ return;
+ }
}
- /** @var $className \yii\db\ActiveRecord */
- $className = $this->className === null ? get_class($object) : Yii::import($this->className);
- $attributeName = $this->attributeName === null ? $attribute : $this->attributeName;
- $query = $className::find();
- $query->where(array($attributeName => $value));
- if (!$query->exists()) {
+ /** @var \yii\db\ActiveRecordInterface $className */
+ if (!$targetClass::find()->where($params)->exists()) {
$this->addError($object, $attribute, $this->message);
}
}
/**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
- * @throws InvalidConfigException if either [[className]] or [[attributeName]] is not set.
+ * @inheritdoc
*/
- public function validateValue($value)
+ protected function validateValue($value)
{
if (is_array($value)) {
- return false;
+ return [$this->message, []];
}
- if ($this->className === null) {
+ if ($this->targetClass === null) {
throw new InvalidConfigException('The "className" property must be set.');
}
- if ($this->attributeName === null) {
- throw new InvalidConfigException('The "attributeName" property must be set.');
+ if (!is_string($this->targetAttribute)) {
+ throw new InvalidConfigException('The "attributeName" property must be configured as a string.');
}
- /** @var $className \yii\db\ActiveRecord */
- $className = $this->className;
- $query = $className::find();
- $query->where(array($this->attributeName => $value));
- return $query->exists();
+
+ /** @var \yii\db\ActiveRecordInterface $targetClass */
+ $targetClass = $this->targetClass;
+ $query = $targetClass::find();
+ $query->where([$this->targetAttribute => $value]);
+ return $query->exists() ? null : [$this->message, []];
}
}
diff --git a/framework/yii/validators/FileValidator.php b/framework/yii/validators/FileValidator.php
index 8a3ab9e..942bf1e 100644
--- a/framework/yii/validators/FileValidator.php
+++ b/framework/yii/validators/FileValidator.php
@@ -13,17 +13,20 @@ use yii\web\UploadedFile;
/**
* FileValidator verifies if an attribute is receiving a valid uploaded file.
*
+ * @property integer $sizeLimit The size limit for uploaded files. This property is read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
class FileValidator extends Validator
{
/**
- * @var mixed a list of file name extensions that are allowed to be uploaded.
+ * @var array|string a list of file name extensions that are allowed to be uploaded.
* This can be either an array or a string consisting of file extension names
* separated by space or comma (e.g. "gif, jpg").
* Extension names are case-insensitive. Defaults to null, meaning all file name
* extensions are allowed.
+ * @see wrongType
*/
public $types;
/**
@@ -44,6 +47,7 @@ class FileValidator extends Validator
* @var integer the maximum file count the given attribute can hold.
* It defaults to 1, meaning single file upload. By defining a higher number,
* multiple uploads become possible.
+ * @see tooMany
*/
public $maxFiles = 1;
/**
@@ -74,9 +78,10 @@ class FileValidator extends Validator
public $tooSmall;
/**
* @var string the error message used when the uploaded file has an extension name
- * that is not listed in [[extensions]]. You may use the following tokens in the message:
+ * that is not listed in [[types]]. You may use the following tokens in the message:
*
* - {attribute}: the attribute name
+ * - {file}: the uploaded file name
* - {extensions}: the list of the allowed extensions.
*/
public $wrongType;
@@ -85,13 +90,12 @@ class FileValidator extends Validator
* You may use the following tokens in the message:
*
* - {attribute}: the attribute name
- * - {file}: the uploaded file name
* - {limit}: the value of [[maxFiles]]
*/
public $tooMany;
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
@@ -120,9 +124,7 @@ class FileValidator extends Validator
}
/**
- * Validates the attribute.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
@@ -133,7 +135,7 @@ class FileValidator extends Validator
return;
}
foreach ($files as $i => $file) {
- if (!$file instanceof UploadedFile || $file->getError() == UPLOAD_ERR_NO_FILE) {
+ if (!$file instanceof UploadedFile || $file->error == UPLOAD_ERR_NO_FILE) {
unset($files[$i]);
}
}
@@ -142,65 +144,61 @@ class FileValidator extends Validator
$this->addError($object, $attribute, $this->uploadRequired);
}
if (count($files) > $this->maxFiles) {
- $this->addError($object, $attribute, $this->tooMany, array('{attribute}' => $attribute, '{limit}' => $this->maxFiles));
+ $this->addError($object, $attribute, $this->tooMany, ['limit' => $this->maxFiles]);
} else {
foreach ($files as $file) {
- $this->validateFile($object, $attribute, $file);
+ $result = $this->validateValue($file);
+ if (!empty($result)) {
+ $this->addError($object, $attribute, $result[0], $result[1]);
+ }
}
}
} else {
- $file = $object->$attribute;
- if ($file instanceof UploadedFile && $file->getError() != UPLOAD_ERR_NO_FILE) {
- $this->validateFile($object, $attribute, $file);
- } else {
- $this->addError($object, $attribute, $this->uploadRequired);
+ $result = $this->validateValue($object->$attribute);
+ if (!empty($result)) {
+ $this->addError($object, $attribute, $result[0], $result[1]);
}
}
}
/**
- * Internally validates a file object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
- * @param UploadedFile $file uploaded file passed to check against a set of rules
+ * @inheritdoc
*/
- protected function validateFile($object, $attribute, $file)
+ protected function validateValue($file)
{
- switch ($file->getError()) {
+ if (!$file instanceof UploadedFile || $file->error == UPLOAD_ERR_NO_FILE) {
+ return [$this->uploadRequired, []];
+ }
+ switch ($file->error) {
case UPLOAD_ERR_OK:
- if ($this->maxSize !== null && $file->getSize() > $this->maxSize) {
- $this->addError($object, $attribute, $this->tooBig, array('{file}' => $file->getName(), '{limit}' => $this->getSizeLimit()));
- }
- if ($this->minSize !== null && $file->getSize() < $this->minSize) {
- $this->addError($object, $attribute, $this->tooSmall, array('{file}' => $file->getName(), '{limit}' => $this->minSize));
- }
- if (!empty($this->types) && !in_array(strtolower(pathinfo($file->getName(), PATHINFO_EXTENSION)), $this->types, true)) {
- $this->addError($object, $attribute, $this->wrongType, array('{file}' => $file->getName(), '{extensions}' => implode(', ', $this->types)));
+ if ($this->maxSize !== null && $file->size > $this->maxSize) {
+ return [$this->tooBig, ['file' => $file->name, 'limit' => $this->getSizeLimit()]];
+ } elseif ($this->minSize !== null && $file->size < $this->minSize) {
+ return [$this->tooSmall, ['file' => $file->name, 'limit' => $this->minSize]];
+ } elseif (!empty($this->types) && !in_array(strtolower(pathinfo($file->name, PATHINFO_EXTENSION)), $this->types, true)) {
+ return [$this->wrongType, ['file' => $file->name, 'extensions' => implode(', ', $this->types)]];
+ } else {
+ return null;
}
- break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
- $this->addError($object, $attribute, $this->tooBig, array('{file}' => $file->getName(), '{limit}' => $this->getSizeLimit()));
- break;
+ return [$this->tooBig, ['file' => $file->name, 'limit' => $this->getSizeLimit()]];
case UPLOAD_ERR_PARTIAL:
- $this->addError($object, $attribute, $this->message);
- Yii::warning('File was only partially uploaded: ' . $file->getName(), __METHOD__);
+ Yii::warning('File was only partially uploaded: ' . $file->name, __METHOD__);
break;
case UPLOAD_ERR_NO_TMP_DIR:
- $this->addError($object, $attribute, $this->message);
- Yii::warning('Missing the temporary folder to store the uploaded file: ' . $file->getName(), __METHOD__);
+ Yii::warning('Missing the temporary folder to store the uploaded file: ' . $file->name, __METHOD__);
break;
case UPLOAD_ERR_CANT_WRITE:
- $this->addError($object, $attribute, $this->message);
- Yii::warning('Failed to write the uploaded file to disk: ' . $file->getName(), __METHOD__);
+ Yii::warning('Failed to write the uploaded file to disk: ' . $file->name, __METHOD__);
break;
case UPLOAD_ERR_EXTENSION:
- $this->addError($object, $attribute, $this->message);
- Yii::warning('File upload was stopped by some PHP extension: ' . $file->getName(), __METHOD__);
+ Yii::warning('File upload was stopped by some PHP extension: ' . $file->name, __METHOD__);
break;
default:
break;
}
+ return [$this->message, []];
}
/**
diff --git a/framework/yii/validators/FilterValidator.php b/framework/yii/validators/FilterValidator.php
index 560feb1..685faee 100644
--- a/framework/yii/validators/FilterValidator.php
+++ b/framework/yii/validators/FilterValidator.php
@@ -46,8 +46,7 @@ class FilterValidator extends Validator
public $skipOnEmpty = false;
/**
- * Initializes the validator.
- * @throws InvalidConfigException if [[filter]] is not set.
+ * @inheritdoc
*/
public function init()
{
@@ -58,11 +57,7 @@ class FilterValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
- * @throws InvalidConfigException if filter property is not a valid callback
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
diff --git a/framework/yii/validators/ImageValidator.php b/framework/yii/validators/ImageValidator.php
new file mode 100644
index 0000000..b4ac1a8
--- /dev/null
+++ b/framework/yii/validators/ImageValidator.php
@@ -0,0 +1,190 @@
+
+ * @since 2.0
+ */
+class ImageValidator extends FileValidator
+{
+ /**
+ * @var string the error message used when the uploaded file is not an image.
+ * You may use the following tokens in the message:
+ *
+ * - {attribute}: the attribute name
+ * - {file}: the uploaded file name
+ */
+ public $notImage;
+ /**
+ * @var integer the minimum width in pixels.
+ * Defaults to null, meaning no limit.
+ * @see underWidth
+ */
+ public $minWidth;
+ /**
+ * @var integer the maximum width in pixels.
+ * Defaults to null, meaning no limit.
+ * @see overWidth
+ */
+ public $maxWidth;
+ /**
+ * @var integer the minimum height in pixels.
+ * Defaults to null, meaning no limit.
+ * @see underHeight
+ */
+ public $minHeight;
+ /**
+ * @var integer the maximum width in pixels.
+ * Defaults to null, meaning no limit.
+ * @see overWidth
+ */
+ public $maxHeight;
+ /**
+ * @var array|string a list of file mime types that are allowed to be uploaded.
+ * This can be either an array or a string consisting of file mime types
+ * separated by space or comma (e.g. "image/jpeg, image/png").
+ * Mime type names are case-insensitive. Defaults to null, meaning all mime types
+ * are allowed.
+ * @see wrongMimeType
+ */
+ public $mimeTypes;
+ /**
+ * @var string the error message used when the image is under [[minWidth]].
+ * You may use the following tokens in the message:
+ *
+ * - {attribute}: the attribute name
+ * - {file}: the uploaded file name
+ * - {limit}: the value of [[minWidth]]
+ */
+ public $underWidth;
+ /**
+ * @var string the error message used when the image is over [[maxWidth]].
+ * You may use the following tokens in the message:
+ *
+ * - {attribute}: the attribute name
+ * - {file}: the uploaded file name
+ * - {limit}: the value of [[maxWidth]]
+ */
+ public $overWidth;
+ /**
+ * @var string the error message used when the image is under [[minHeight]].
+ * You may use the following tokens in the message:
+ *
+ * - {attribute}: the attribute name
+ * - {file}: the uploaded file name
+ * - {limit}: the value of [[minHeight]]
+ */
+ public $underHeight;
+ /**
+ * @var string the error message used when the image is over [[maxHeight]].
+ * You may use the following tokens in the message:
+ *
+ * - {attribute}: the attribute name
+ * - {file}: the uploaded file name
+ * - {limit}: the value of [[maxHeight]]
+ */
+ public $overHeight;
+ /**
+ * @var string the error message used when the file has an mime type
+ * that is not listed in [[mimeTypes]].
+ * You may use the following tokens in the message:
+ *
+ * - {attribute}: the attribute name
+ * - {file}: the uploaded file name
+ * - {mimeTypes}: the value of [[mimeTypes]]
+ */
+ public $wrongMimeType;
+
+ /**
+ * @inheritdoc
+ */
+ public function init()
+ {
+ parent::init();
+
+ if ($this->notImage === null) {
+ $this->notImage = Yii::t('yii', 'The file "{file}" is not an image.');
+ }
+ if ($this->underWidth === null) {
+ $this->underWidth = Yii::t('yii', 'The file "{file}" is too small. The width cannot be smaller than {limit} pixels.');
+ }
+ if ($this->underHeight === null) {
+ $this->underHeight = Yii::t('yii', 'The file "{file}" is too small. The height cannot be smaller than {limit} pixels.');
+ }
+ if ($this->overWidth === null) {
+ $this->overWidth = Yii::t('yii', 'The file "{file}" is too large. The width cannot be larger than {limit} pixels.');
+ }
+ if ($this->overHeight === null) {
+ $this->overHeight = Yii::t('yii', 'The file "{file}" is too large. The height cannot be larger than {limit} pixels.');
+ }
+ if ($this->wrongMimeType === null) {
+ $this->wrongMimeType = Yii::t('yii', 'Only files with these mimeTypes are allowed: {mimeTypes}.');
+ }
+ if (!is_array($this->mimeTypes)) {
+ $this->mimeTypes = preg_split('/[\s,]+/', strtolower($this->mimeTypes), -1, PREG_SPLIT_NO_EMPTY);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function validateValue($file)
+ {
+ $result = parent::validateValue($file);
+ return empty($result) ? $this->validateImage($file) : $result;
+ }
+
+ /**
+ * Validates an image file.
+ * @param UploadedFile $image uploaded file passed to check against a set of rules
+ * @return array|null the error message and the parameters to be inserted into the error message.
+ * Null should be returned if the data is valid.
+ */
+ protected function validateImage($image)
+ {
+ if (!empty($this->mimeTypes) && !in_array(FileHelper::getMimeType($image->tempName), $this->mimeTypes, true)) {
+ return [$this->wrongMimeType, ['file' => $image->name, 'mimeTypes' => implode(', ', $this->mimeTypes)]];
+ }
+
+ if (false === ($imageInfo = getimagesize($image->tempName))) {
+ return [$this->notImage, ['file' => $image->name]];
+ }
+
+ list($width, $height, $type) = $imageInfo;
+
+ if ($width == 0 || $height == 0) {
+ return [$this->notImage, ['file' => $image->name]];
+ }
+
+ if ($this->minWidth !== null && $width < $this->minWidth) {
+ return [$this->underWidth, ['file' => $image->name, 'limit' => $this->minWidth]];
+ }
+
+ if ($this->minHeight !== null && $height < $this->minHeight) {
+ return [$this->underHeight, ['file' => $image->name, 'limit' => $this->minHeight]];
+ }
+
+ if ($this->maxWidth !== null && $width > $this->maxWidth) {
+ return [$this->overWidth, ['file' => $image->name, 'limit' => $this->maxWidth]];
+ }
+
+ if ($this->maxHeight !== null && $height > $this->maxHeight) {
+ return [$this->overHeight, ['file' => $image->name, 'limit' => $this->maxHeight]];
+ }
+ return null;
+ }
+}
diff --git a/framework/yii/validators/InlineValidator.php b/framework/yii/validators/InlineValidator.php
index dd951aa..b769e4e 100644
--- a/framework/yii/validators/InlineValidator.php
+++ b/framework/yii/validators/InlineValidator.php
@@ -26,8 +26,11 @@ class InlineValidator extends Validator
{
/**
* @var string|\Closure an anonymous function or the name of a model class method that will be
- * called to perform the actual validation. Note that if you use anonymous function, you cannot
- * use `$this` in it unless you are using PHP 5.4 or above.
+ * called to perform the actual validation. The signature of the method should be like the following:
+ *
+ * ~~~
+ * function foo($attribute, $params)
+ * ~~~
*/
public $method;
/**
@@ -39,7 +42,7 @@ class InlineValidator extends Validator
* The signature of the method should be like the following:
*
* ~~~
- * function foo($attribute)
+ * function foo($attribute, $params)
* {
* return "javascript";
* }
@@ -52,48 +55,28 @@ class InlineValidator extends Validator
public $clientValidate;
/**
- * Validates the attribute of the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
$method = $this->method;
if (is_string($method)) {
- $method = array($object, $method);
+ $method = [$object, $method];
}
call_user_func($method, $attribute, $this->params);
}
/**
- * Returns the JavaScript needed for performing client-side validation.
- *
- * You may override this method to return the JavaScript validation code if
- * the validator can support client-side validation.
- *
- * The following JavaScript variables are predefined and can be used in the validation code:
- *
- * - `attribute`: the name of the attribute being validated.
- * - `value`: the value being validated.
- * - `messages`: an array used to hold the validation error messages for the attribute.
- *
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @return string the client-side validation script. Null if the validator does not support
- * client-side validation.
- * @see enableClientValidation
- * @see \yii\web\ActiveForm::enableClientValidation
+ * @inheritdoc
*/
public function clientValidateAttribute($object, $attribute, $view)
{
if ($this->clientValidate !== null) {
$method = $this->clientValidate;
if (is_string($method)) {
- $method = array($object, $method);
+ $method = [$object, $method];
}
- return call_user_func($method, $attribute);
+ return call_user_func($method, $attribute, $this->params);
} else {
return null;
}
diff --git a/framework/yii/validators/NumberValidator.php b/framework/yii/validators/NumberValidator.php
index 3bcc6ce..1bb2360 100644
--- a/framework/yii/validators/NumberValidator.php
+++ b/framework/yii/validators/NumberValidator.php
@@ -56,7 +56,7 @@ class NumberValidator extends Validator
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
@@ -74,10 +74,7 @@ class NumberValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
@@ -91,67 +88,66 @@ class NumberValidator extends Validator
$this->addError($object, $attribute, $this->message);
}
if ($this->min !== null && $value < $this->min) {
- $this->addError($object, $attribute, $this->tooSmall, array('{min}' => $this->min));
+ $this->addError($object, $attribute, $this->tooSmall, ['min' => $this->min]);
}
if ($this->max !== null && $value > $this->max) {
- $this->addError($object, $attribute, $this->tooBig, array('{max}' => $this->max));
+ $this->addError($object, $attribute, $this->tooBig, ['max' => $this->max]);
}
}
/**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
+ * @inheritdoc
*/
- public function validateValue($value)
+ protected function validateValue($value)
{
- return preg_match($this->integerOnly ? $this->integerPattern : $this->numberPattern, "$value")
- && ($this->min === null || $value >= $this->min)
- && ($this->max === null || $value <= $this->max);
+ if (is_array($value)) {
+ return [Yii::t('yii', '{attribute} is invalid.'), []];
+ }
+ $pattern = $this->integerOnly ? $this->integerPattern : $this->numberPattern;
+ if (!preg_match($pattern, "$value")) {
+ return [$this->message, []];
+ } elseif ($this->min !== null && $value < $this->min) {
+ return [$this->tooSmall, ['min' => $this->min]];
+ } elseif ($this->max !== null && $value > $this->max) {
+ return [$this->tooBig, ['max' => $this->max]];
+ } else {
+ return null;
+ }
}
/**
- * Returns the JavaScript needed for performing client-side validation.
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @return string the client-side validation script.
+ * @inheritdoc
*/
public function clientValidateAttribute($object, $attribute, $view)
{
$label = $object->getAttributeLabel($attribute);
- $value = $object->$attribute;
- $options = array(
+ $options = [
'pattern' => new JsExpression($this->integerOnly ? $this->integerPattern : $this->numberPattern),
- 'message' => Html::encode(strtr($this->message, array(
+ 'message' => strtr($this->message, [
'{attribute}' => $label,
- '{value}' => $value,
- ))),
- );
+ ]),
+ ];
if ($this->min !== null) {
$options['min'] = $this->min;
- $options['tooSmall'] = Html::encode(strtr($this->tooSmall, array(
+ $options['tooSmall'] = strtr($this->tooSmall, [
'{attribute}' => $label,
- '{value}' => $value,
'{min}' => $this->min,
- )));
+ ]);
}
if ($this->max !== null) {
$options['max'] = $this->max;
- $options['tooBig'] = Html::encode(strtr($this->tooBig, array(
+ $options['tooBig'] = strtr($this->tooBig, [
'{attribute}' => $label,
- '{value}' => $value,
'{max}' => $this->max,
- )));
+ ]);
}
if ($this->skipOnEmpty) {
$options['skipOnEmpty'] = 1;
}
- $view->registerAssetBundle('yii/validation');
+ ValidationAsset::register($view);
return 'yii.validation.number(value, messages, ' . Json::encode($options) . ');';
}
}
diff --git a/framework/yii/validators/PunycodeAsset.php b/framework/yii/validators/PunycodeAsset.php
new file mode 100644
index 0000000..c0c1e2b
--- /dev/null
+++ b/framework/yii/validators/PunycodeAsset.php
@@ -0,0 +1,23 @@
+
+ * @since 2.0
+ */
+class PunycodeAsset extends AssetBundle
+{
+ public $sourcePath = '@yii/assets';
+ public $js = [
+ 'punycode/punycode.js',
+ ];
+}
diff --git a/framework/yii/validators/RangeValidator.php b/framework/yii/validators/RangeValidator.php
index 90256df..a4da139 100644
--- a/framework/yii/validators/RangeValidator.php
+++ b/framework/yii/validators/RangeValidator.php
@@ -38,8 +38,7 @@ class RangeValidator extends Validator
public $not = false;
/**
- * Initializes the validator.
- * @throws InvalidConfigException if [[range]] is not set.
+ * @inheritdoc
*/
public function init()
{
@@ -53,57 +52,36 @@ class RangeValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
- public function validateAttribute($object, $attribute)
+ protected function validateValue($value)
{
- $value = $object->$attribute;
- if (!$this->validateValue($value)) {
- $this->addError($object, $attribute, $this->message);
- }
- }
-
- /**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
- */
- public function validateValue($value)
- {
- return !$this->not && in_array($value, $this->range, $this->strict)
+ $valid = !$this->not && in_array($value, $this->range, $this->strict)
|| $this->not && !in_array($value, $this->range, $this->strict);
+ return $valid ? null : [$this->message, []];
}
/**
- * Returns the JavaScript needed for performing client-side validation.
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @return string the client-side validation script.
+ * @inheritdoc
*/
public function clientValidateAttribute($object, $attribute, $view)
{
- $range = array();
+ $range = [];
foreach ($this->range as $value) {
$range[] = (string)$value;
}
- $options = array(
+ $options = [
'range' => $range,
'not' => $this->not,
- 'message' => Html::encode(strtr($this->message, array(
+ 'message' => strtr($this->message, [
'{attribute}' => $object->getAttributeLabel($attribute),
- '{value}' => $object->$attribute,
- ))),
- );
+ ]),
+ ];
if ($this->skipOnEmpty) {
$options['skipOnEmpty'] = 1;
}
- $view->registerAssetBundle('yii/validation');
+ ValidationAsset::register($view);
return 'yii.validation.range(value, messages, ' . json_encode($options) . ');';
}
}
diff --git a/framework/yii/validators/RegularExpressionValidator.php b/framework/yii/validators/RegularExpressionValidator.php
index 72a5a74..28e9bdc 100644
--- a/framework/yii/validators/RegularExpressionValidator.php
+++ b/framework/yii/validators/RegularExpressionValidator.php
@@ -30,13 +30,11 @@ class RegularExpressionValidator extends Validator
/**
* @var boolean whether to invert the validation logic. Defaults to false. If set to true,
* the regular expression defined via [[pattern]] should NOT match the attribute value.
- * @throws InvalidConfigException if the "pattern" is not a valid regular expression
**/
public $not = false;
/**
- * Initializes the validator.
- * @throws InvalidConfigException if [[pattern]] is not set.
+ * @inheritdoc
*/
public function init()
{
@@ -50,39 +48,18 @@ class RegularExpressionValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
- public function validateAttribute($object, $attribute)
+ protected function validateValue($value)
{
- $value = $object->$attribute;
- if (!$this->validateValue($value)) {
- $this->addError($object, $attribute, $this->message);
- }
- }
-
- /**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
- */
- public function validateValue($value)
- {
- return !is_array($value) &&
+ $valid = !is_array($value) &&
(!$this->not && preg_match($this->pattern, $value)
|| $this->not && !preg_match($this->pattern, $value));
+ return $valid ? null : [$this->message, []];
}
/**
- * Returns the JavaScript needed for performing client-side validation.
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @return string the client-side validation script.
- * @throws InvalidConfigException if the "pattern" is not a valid regular expression
+ * @inheritdoc
*/
public function clientValidateAttribute($object, $attribute, $view)
{
@@ -100,19 +77,18 @@ class RegularExpressionValidator extends Validator
$pattern .= preg_replace('/[^igm]/', '', $flag);
}
- $options = array(
+ $options = [
'pattern' => new JsExpression($pattern),
'not' => $this->not,
- 'message' => Html::encode(strtr($this->message, array(
+ 'message' => strtr($this->message, [
'{attribute}' => $object->getAttributeLabel($attribute),
- '{value}' => $object->$attribute,
- ))),
- );
+ ]),
+ ];
if ($this->skipOnEmpty) {
$options['skipOnEmpty'] = 1;
}
- $view->registerAssetBundle('yii/validation');
+ ValidationAsset::register($view);
return 'yii.validation.regularExpression(value, messages, ' . Json::encode($options) . ');';
}
}
diff --git a/framework/yii/validators/RequiredValidator.php b/framework/yii/validators/RequiredValidator.php
index aedbe05..f291f39 100644
--- a/framework/yii/validators/RequiredValidator.php
+++ b/framework/yii/validators/RequiredValidator.php
@@ -51,7 +51,7 @@ class RequiredValidator extends Validator
public $message;
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
@@ -63,56 +63,36 @@ class RequiredValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
- public function validateAttribute($object, $attribute)
- {
- if (!$this->validateValue($object->$attribute)) {
- if ($this->requiredValue === null) {
- $this->addError($object, $attribute, $this->message);
- } else {
- $this->addError($object, $attribute, $this->message, array(
- '{requiredValue}' => $this->requiredValue,
- ));
- }
- }
- }
-
- /**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
- */
- public function validateValue($value)
+ protected function validateValue($value)
{
if ($this->requiredValue === null) {
if ($this->strict && $value !== null || !$this->strict && !$this->isEmpty($value, true)) {
- return true;
+ return null;
}
} elseif (!$this->strict && $value == $this->requiredValue || $this->strict && $value === $this->requiredValue) {
- return true;
+ return null;
+ }
+ if ($this->requiredValue === null) {
+ return [$this->message, []];
+ } else {
+ return [$this->message, [
+ 'requiredValue' => $this->requiredValue,
+ ]];
}
- return false;
}
/**
- * Returns the JavaScript needed for performing client-side validation.
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @return string the client-side validation script.
+ * @inheritdoc
*/
public function clientValidateAttribute($object, $attribute, $view)
{
- $options = array();
+ $options = [];
if ($this->requiredValue !== null) {
- $options['message'] = strtr($this->message, array(
+ $options['message'] = strtr($this->message, [
'{requiredValue}' => $this->requiredValue,
- ));
+ ]);
$options['requiredValue'] = $this->requiredValue;
} else {
$options['message'] = $this->message;
@@ -121,12 +101,11 @@ class RequiredValidator extends Validator
$options['strict'] = 1;
}
- $options['message'] = Html::encode(strtr($options['message'], array(
+ $options['message'] = strtr($options['message'], [
'{attribute}' => $object->getAttributeLabel($attribute),
- '{value}' => $object->$attribute,
- )));
+ ]);
- $view->registerAssetBundle('yii/validation');
+ ValidationAsset::register($view);
return 'yii.validation.required(value, messages, ' . json_encode($options) . ');';
}
}
diff --git a/framework/yii/validators/SafeValidator.php b/framework/yii/validators/SafeValidator.php
new file mode 100644
index 0000000..25da899
--- /dev/null
+++ b/framework/yii/validators/SafeValidator.php
@@ -0,0 +1,24 @@
+
+ * @since 2.0
+ */
+class SafeValidator extends Validator
+{
+ /**
+ * @inheritdoc
+ */
+ public function validateAttribute($object, $attribute)
+ {
+ }
+}
diff --git a/framework/yii/validators/StringValidator.php b/framework/yii/validators/StringValidator.php
index e06354b..279a189 100644
--- a/framework/yii/validators/StringValidator.php
+++ b/framework/yii/validators/StringValidator.php
@@ -21,17 +21,24 @@ use yii\helpers\Html;
class StringValidator extends Validator
{
/**
- * @var integer maximum length. Defaults to null, meaning no maximum limit.
+ * @var integer|array specifies the length limit of the value to be validated.
+ * This can be specified in one of the following forms:
+ *
+ * - an integer: the exact length that the value should be of;
+ * - an array of one element: the minimum length that the value should be of. For example, `[8]`.
+ * This will overwrite [[min]].
+ * - an array of two elements: the minimum and maximum lengths that the value should be of.
+ * For example, `[8, 128]`. This will overwrite both [[min]] and [[max]].
*/
- public $max;
+ public $length;
/**
- * @var integer minimum length. Defaults to null, meaning no minimum limit.
+ * @var integer maximum length. If not set, it means no maximum length limit.
*/
- public $min;
+ public $max;
/**
- * @var integer exact length. Defaults to null, meaning no exact length limit.
+ * @var integer minimum length. If not set, it means no minimum length limit.
*/
- public $is;
+ public $min;
/**
* @var string user-defined error message used when the value is not a string
*/
@@ -45,7 +52,7 @@ class StringValidator extends Validator
*/
public $tooLong;
/**
- * @var string user-defined error message used when the length of the value is not equal to [[is]].
+ * @var string user-defined error message used when the length of the value is not equal to [[length]].
*/
public $notEqual;
/**
@@ -56,11 +63,20 @@ class StringValidator extends Validator
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
parent::init();
+ if (is_array($this->length)) {
+ if (isset($this->length[0])) {
+ $this->min = $this->length[0];
+ }
+ if (isset($this->length[1])) {
+ $this->max = $this->length[1];
+ }
+ $this->length = null;
+ }
if ($this->encoding === null) {
$this->encoding = Yii::$app->charset;
}
@@ -73,16 +89,13 @@ class StringValidator extends Validator
if ($this->max !== null && $this->tooLong === null) {
$this->tooLong = Yii::t('yii', '{attribute} should contain at most {max} characters.');
}
- if ($this->is !== null && $this->notEqual === null) {
+ if ($this->length !== null && $this->notEqual === null) {
$this->notEqual = Yii::t('yii', '{attribute} should contain {length} characters.');
}
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
@@ -96,81 +109,79 @@ class StringValidator extends Validator
$length = mb_strlen($value, $this->encoding);
if ($this->min !== null && $length < $this->min) {
- $this->addError($object, $attribute, $this->tooShort, array('{min}' => $this->min));
+ $this->addError($object, $attribute, $this->tooShort, ['min' => $this->min]);
}
if ($this->max !== null && $length > $this->max) {
- $this->addError($object, $attribute, $this->tooLong, array('{max}' => $this->max));
+ $this->addError($object, $attribute, $this->tooLong, ['max' => $this->max]);
}
- if ($this->is !== null && $length !== $this->is) {
- $this->addError($object, $attribute, $this->notEqual, array('{length}' => $this->is));
+ if ($this->length !== null && $length !== $this->length) {
+ $this->addError($object, $attribute, $this->notEqual, ['length' => $this->length]);
}
}
/**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
+ * @inheritdoc
*/
- public function validateValue($value)
+ protected function validateValue($value)
{
if (!is_string($value)) {
- return false;
+ return [$this->message, []];
}
+
$length = mb_strlen($value, $this->encoding);
- return ($this->min === null || $length >= $this->min)
- && ($this->max === null || $length <= $this->max)
- && ($this->is === null || $length === $this->is);
+
+ if ($this->min !== null && $length < $this->min) {
+ return [$this->tooShort, ['min' => $this->min]];
+ }
+ if ($this->max !== null && $length > $this->max) {
+ return [$this->tooLong, ['max' => $this->max]];
+ }
+ if ($this->length !== null && $length !== $this->length) {
+ return [$this->notEqual, ['length' => $this->length]];
+ }
+
+ return null;
}
/**
- * Returns the JavaScript needed for performing client-side validation.
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @return string the client-side validation script.
+ * @inheritdoc
*/
public function clientValidateAttribute($object, $attribute, $view)
{
$label = $object->getAttributeLabel($attribute);
- $value = $object->$attribute;
- $options = array(
- 'message' => Html::encode(strtr($this->message, array(
+ $options = [
+ 'message' => strtr($this->message, [
'{attribute}' => $label,
- '{value}' => $value,
- ))),
- );
+ ]),
+ ];
if ($this->min !== null) {
$options['min'] = $this->min;
- $options['tooShort'] = Html::encode(strtr($this->tooShort, array(
+ $options['tooShort'] = strtr($this->tooShort, [
'{attribute}' => $label,
- '{value}' => $value,
'{min}' => $this->min,
- )));
+ ]);
}
if ($this->max !== null) {
$options['max'] = $this->max;
- $options['tooLong'] = Html::encode(strtr($this->tooLong, array(
+ $options['tooLong'] = strtr($this->tooLong, [
'{attribute}' => $label,
- '{value}' => $value,
'{max}' => $this->max,
- )));
+ ]);
}
- if ($this->is !== null) {
- $options['is'] = $this->is;
- $options['notEqual'] = Html::encode(strtr($this->notEqual, array(
+ if ($this->length !== null) {
+ $options['is'] = $this->length;
+ $options['notEqual'] = strtr($this->notEqual, [
'{attribute}' => $label,
- '{value}' => $value,
- '{length}' => $this->is,
- )));
+ '{length}' => $this->length,
+ ]);
}
if ($this->skipOnEmpty) {
$options['skipOnEmpty'] = 1;
}
- $view->registerAssetBundle('yii/validation');
+ ValidationAsset::register($view);
return 'yii.validation.string(value, messages, ' . json_encode($options) . ');';
}
}
diff --git a/framework/yii/validators/UniqueValidator.php b/framework/yii/validators/UniqueValidator.php
index b650693..51474b0 100644
--- a/framework/yii/validators/UniqueValidator.php
+++ b/framework/yii/validators/UniqueValidator.php
@@ -8,11 +8,28 @@
namespace yii\validators;
use Yii;
-use yii\base\InvalidConfigException;
-use yii\db\ActiveRecord;
+use yii\db\ActiveRecordInterface;
/**
- * CUniqueValidator validates that the attribute value is unique in the corresponding database table.
+ * UniqueValidator validates that the attribute value is unique in the specified database table.
+ *
+ * UniqueValidator checks if the value being validated is unique in the table column specified by
+ * the ActiveRecord class [[targetClass]] and the attribute [[targetAttribute]].
+ *
+ * The followings are examples of validation rules using this validator:
+ *
+ * ```php
+ * // a1 needs to be unique
+ * ['a1', 'unique']
+ * // a1 needs to be unique, but column a2 will be used to check the uniqueness of the a1 value
+ * ['a1', 'unique', 'targetAttribute' => 'a2']
+ * // a1 and a2 need to unique together, and they both will receive error message
+ * [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']]
+ * // a1 and a2 need to unique together, only a1 will receive error message
+ * ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']]
+ * // a1 needs to be unique by checking the uniqueness of both a2 and a3 (using a1 value)
+ * ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']]
+ * ```
*
* @author Qiang Xue
* @since 2.0
@@ -20,21 +37,23 @@ use yii\db\ActiveRecord;
class UniqueValidator extends Validator
{
/**
- * @var string the ActiveRecord class name or alias of the class
- * that should be used to look for the attribute value being validated.
- * Defaults to null, meaning using the ActiveRecord class of the attribute being validated.
- * @see attributeName
+ * @var string the name of the ActiveRecord class that should be used to validate the uniqueness
+ * of the current attribute value. It not set, it will use the ActiveRecord class of the attribute being validated.
+ * @see targetAttribute
*/
- public $className;
+ public $targetClass;
/**
- * @var string the ActiveRecord class attribute name that should be
- * used to look for the attribute value being validated. Defaults to null,
- * meaning using the name of the attribute being validated.
+ * @var string|array the name of the ActiveRecord attribute that should be used to
+ * validate the uniqueness of the current attribute value. If not set, it will use the name
+ * of the attribute currently being validated. You may use an array to validate the uniqueness
+ * of multiple columns at the same time. The array values are the attributes that will be
+ * used to validate the uniqueness, while the array keys are the attributes whose values are to be validated.
+ * If the key and the value are the same, you can just specify the value.
*/
- public $attributeName;
+ public $targetAttribute;
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
@@ -45,49 +64,52 @@ class UniqueValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\db\ActiveRecord $object the object being validated
- * @param string $attribute the attribute being validated
- * @throws InvalidConfigException if table doesn't have column specified
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
- $value = $object->$attribute;
+ /** @var ActiveRecordInterface $targetClass */
+ $targetClass = $this->targetClass === null ? get_class($object) : $this->targetClass;
+ $targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;
- if (is_array($value)) {
- $this->addError($object, $attribute, Yii::t('yii', '{attribute} is invalid.'));
- return;
+ if (is_array($targetAttribute)) {
+ $params = [];
+ foreach ($targetAttribute as $k => $v) {
+ $params[$v] = is_integer($k) ? $object->$v : $object->$k;
+ }
+ } else {
+ $params = [$targetAttribute => $object->$attribute];
}
- /** @var $className \yii\db\ActiveRecord */
- $className = $this->className === null ? get_class($object) : Yii::import($this->className);
- $attributeName = $this->attributeName === null ? $attribute : $this->attributeName;
-
- $table = $className::getTableSchema();
- if (($column = $table->getColumn($attributeName)) === null) {
- throw new InvalidConfigException("Table '{$table->name}' does not have a column named '$attributeName'.");
+ foreach ($params as $value) {
+ if (is_array($value)) {
+ $this->addError($object, $attribute, Yii::t('yii', '{attribute} is invalid.'));
+ return;
+ }
}
- $query = $className::find();
- $query->where(array($column->name => $value));
+ $query = $targetClass::find();
+ $query->where($params);
- if (!$object instanceof ActiveRecord || $object->getIsNewRecord()) {
+ if (!$object instanceof ActiveRecordInterface || $object->getIsNewRecord()) {
// if current $object isn't in the database yet then it's OK just to call exists()
$exists = $query->exists();
} else {
// if current $object is in the database already we can't use exists()
- $query->limit(2);
- $objects = $query->all();
-
+ /** @var ActiveRecordInterface[] $objects */
+ $objects = $query->limit(2)->all();
$n = count($objects);
if ($n === 1) {
- if ($column->isPrimaryKey) {
+ $keys = array_keys($params);
+ $pks = $targetClass::primaryKey();
+ sort($keys);
+ sort($pks);
+ if ($keys === $pks) {
// primary key is modified and not unique
$exists = $object->getOldPrimaryKey() != $object->getPrimaryKey();
} else {
// non-primary key, need to exclude the current record based on PK
- $exists = array_shift($objects)->getPrimaryKey() != $object->getOldPrimaryKey();
+ $exists = $objects[0]->getPrimaryKey() != $object->getOldPrimaryKey();
}
} else {
$exists = $n > 1;
diff --git a/framework/yii/validators/UrlValidator.php b/framework/yii/validators/UrlValidator.php
index 18f2f45..4cb20f6 100644
--- a/framework/yii/validators/UrlValidator.php
+++ b/framework/yii/validators/UrlValidator.php
@@ -16,6 +16,9 @@ use yii\helpers\Json;
/**
* UrlValidator validates that the attribute value is a valid http or https URL.
*
+ * Note that this validator only checks if the URL scheme and host part are correct.
+ * It does not check the rest part of a URL.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -31,7 +34,7 @@ class UrlValidator extends Validator
* @var array list of URI schemes which should be considered valid. By default, http and https
* are considered to be valid schemes.
**/
- public $validSchemes = array('http', 'https');
+ public $validSchemes = ['http', 'https'];
/**
* @var string the default URI scheme. If the input doesn't contain the scheme part, the default
* scheme will be prepended to it (thus changing the input). Defaults to null, meaning a URL must
@@ -48,7 +51,7 @@ class UrlValidator extends Validator
/**
- * Initializes the validator.
+ * @inheritdoc
*/
public function init()
{
@@ -62,29 +65,23 @@ class UrlValidator extends Validator
}
/**
- * Validates the attribute of the object.
- * If there is any error, the error message is added to the object.
- * @param \yii\base\Model $object the object being validated
- * @param string $attribute the attribute being validated
+ * @inheritdoc
*/
public function validateAttribute($object, $attribute)
{
$value = $object->$attribute;
- if ($this->validateValue($value)) {
- if ($this->defaultScheme !== null && strpos($value, '://') === false) {
- $object->$attribute = $this->defaultScheme . '://' . $value;
- }
- } else {
- $this->addError($object, $attribute, $this->message);
+ $result = $this->validateValue($value);
+ if (!empty($result)) {
+ $this->addError($object, $attribute, $result[0], $result[1]);
+ } elseif ($this->defaultScheme !== null && strpos($value, '://') === false) {
+ $object->$attribute = $this->defaultScheme . '://' . $value;
}
}
/**
- * Validates the given value.
- * @param mixed $value the value to be validated.
- * @return boolean whether the value is valid.
+ * @inheritdoc
*/
- public function validateValue($value)
+ protected function validateValue($value)
{
// make sure the length is limited to avoid DOS attacks
if (is_string($value) && strlen($value) < 2000) {
@@ -105,20 +102,14 @@ class UrlValidator extends Validator
}
if (preg_match($pattern, $value)) {
- return true;
+ return null;
}
}
- return false;
+ return [$this->message, []];
}
/**
- * Returns the JavaScript needed for performing client-side validation.
- * @param \yii\base\Model $object the data object being validated
- * @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
- * containing a model form with this validator applied.
- * @return string the client-side validation script.
- * @see \yii\Web\ActiveForm::enableClientValidation
+ * @inheritdoc
*/
public function clientValidateAttribute($object, $attribute, $view)
{
@@ -128,14 +119,13 @@ class UrlValidator extends Validator
$pattern = $this->pattern;
}
- $options = array(
+ $options = [
'pattern' => new JsExpression($pattern),
- 'message' => Html::encode(strtr($this->message, array(
+ 'message' => strtr($this->message, [
'{attribute}' => $object->getAttributeLabel($attribute),
- '{value}' => $object->$attribute,
- ))),
+ ]),
'enableIDN' => (boolean)$this->enableIDN,
- );
+ ];
if ($this->skipOnEmpty) {
$options['skipOnEmpty'] = 1;
}
@@ -143,9 +133,9 @@ class UrlValidator extends Validator
$options['defaultScheme'] = $this->defaultScheme;
}
- $view->registerAssetBundle('yii/validation');
+ ValidationAsset::register($view);
if ($this->enableIDN) {
- $view->registerAssetBundle('yii/punycode');
+ PunycodeAsset::register($view);
}
return 'yii.validation.url(value, messages, ' . Json::encode($options) . ');';
}
diff --git a/framework/yii/validators/ValidationAsset.php b/framework/yii/validators/ValidationAsset.php
new file mode 100644
index 0000000..14d7ad0
--- /dev/null
+++ b/framework/yii/validators/ValidationAsset.php
@@ -0,0 +1,26 @@
+
+ * @since 2.0
+ */
+class ValidationAsset extends AssetBundle
+{
+ public $sourcePath = '@yii/assets';
+ public $js = [
+ 'yii.validation.js',
+ ];
+ public $depends = [
+ 'yii\web\YiiAsset',
+ ];
+}
diff --git a/framework/yii/validators/Validator.php b/framework/yii/validators/Validator.php
index 2629002..4b04b87 100644
--- a/framework/yii/validators/Validator.php
+++ b/framework/yii/validators/Validator.php
@@ -14,7 +14,7 @@ use yii\base\NotSupportedException;
/**
* Validator is the base class for all validators.
*
- * Child classes should override the [[validateAttribute()]] method to provide the actual
+ * Child classes should override the [[validateValue()]] and/or [[validateAttribute()]] methods to provide the actual
* logic of performing data validation. Child classes may also override [[clientValidateAttribute()]]
* to provide client-side validation support.
*
@@ -31,25 +31,28 @@ use yii\base\NotSupportedException;
* - `exist`: [[ExistValidator]]
* - `file`: [[FileValidator]]
* - `filter`: [[FilterValidator]]
+ * - `image`: [[ImageValidator]]
* - `in`: [[RangeValidator]]
* - `integer`: [[NumberValidator]]
* - `match`: [[RegularExpressionValidator]]
* - `required`: [[RequiredValidator]]
+ * - `safe`: [[SafeValidator]]
* - `string`: [[StringValidator]]
+ * - `trim`: [[FilterValidator]]
* - `unique`: [[UniqueValidator]]
* - `url`: [[UrlValidator]]
*
* @author Qiang Xue
* @since 2.0
*/
-abstract class Validator extends Component
+class Validator extends Component
{
/**
* @var array list of built-in validators (name => class or configuration)
*/
- public static $builtInValidators = array(
+ public static $builtInValidators = [
'boolean' => 'yii\validators\BooleanValidator',
- 'captcha' => 'yii\validators\CaptchaValidator',
+ 'captcha' => 'yii\captcha\CaptchaValidator',
'compare' => 'yii\validators\CompareValidator',
'date' => 'yii\validators\DateValidator',
'default' => 'yii\validators\DefaultValueValidator',
@@ -58,23 +61,30 @@ abstract class Validator extends Component
'exist' => 'yii\validators\ExistValidator',
'file' => 'yii\validators\FileValidator',
'filter' => 'yii\validators\FilterValidator',
+ 'image' => 'yii\validators\ImageValidator',
'in' => 'yii\validators\RangeValidator',
- 'integer' => array(
+ 'integer' => [
'class' => 'yii\validators\NumberValidator',
'integerOnly' => true,
- ),
+ ],
'match' => 'yii\validators\RegularExpressionValidator',
'number' => 'yii\validators\NumberValidator',
'required' => 'yii\validators\RequiredValidator',
+ 'safe' => 'yii\validators\SafeValidator',
'string' => 'yii\validators\StringValidator',
+ 'trim' => [
+ 'class' => 'yii\validators\FilterValidator',
+ 'filter' => 'trim',
+ ],
'unique' => 'yii\validators\UniqueValidator',
'url' => 'yii\validators\UrlValidator',
- );
+ ];
/**
- * @var array list of attributes to be validated.
+ * @var array|string attributes to be validated by this validator. For multiple attributes,
+ * please specify them as an array; for single attribute, you may use either a string or an array.
*/
- public $attributes;
+ public $attributes = [];
/**
* @var string the user-defined error message. It may contain the following placeholders which
* will be replaced accordingly by the validator:
@@ -84,13 +94,15 @@ abstract class Validator extends Component
*/
public $message;
/**
- * @var array list of scenarios that the validator can be applied to.
+ * @var array|string scenarios that the validator can be applied to. For multiple scenarios,
+ * please specify them as an array; for single scenario, you may use either a string or an array.
*/
- public $on = array();
+ public $on = [];
/**
- * @var array list of scenarios that the validator should not be applied to.
+ * @var array|string scenarios that the validator should not be applied to. For multiple scenarios,
+ * please specify them as an array; for single scenario, you may use either a string or an array.
*/
- public $except = array();
+ public $except = [];
/**
* @var boolean whether this validation rule should be skipped if the attribute being validated
* already has some validation error according to some previous rules. Defaults to true.
@@ -109,13 +121,6 @@ abstract class Validator extends Component
*/
public $enableClientValidation = true;
- /**
- * Validates a single attribute.
- * Child classes must implement this method to provide the actual validation logic.
- * @param \yii\base\Model $object the data object to be validated
- * @param string $attribute the name of the attribute to be validated.
- */
- abstract public function validateAttribute($object, $attribute);
/**
* Creates a validator object.
@@ -127,28 +132,17 @@ abstract class Validator extends Component
* @param array $params initial values to be applied to the validator properties
* @return Validator the validator
*/
- public static function createValidator($type, $object, $attributes, $params = array())
+ public static function createValidator($type, $object, $attributes, $params = [])
{
- if (!is_array($attributes)) {
- $attributes = preg_split('/[\s,]+/', $attributes, -1, PREG_SPLIT_NO_EMPTY);
- }
$params['attributes'] = $attributes;
- if (isset($params['on']) && !is_array($params['on'])) {
- $params['on'] = preg_split('/[\s,]+/', $params['on'], -1, PREG_SPLIT_NO_EMPTY);
- }
-
- if (isset($params['except']) && !is_array($params['except'])) {
- $params['except'] = preg_split('/[\s,]+/', $params['except'], -1, PREG_SPLIT_NO_EMPTY);
- }
-
if (method_exists($object, $type)) {
// method-based validator
$params['class'] = __NAMESPACE__ . '\InlineValidator';
$params['method'] = $type;
} else {
- if (isset(self::$builtInValidators[$type])) {
- $type = self::$builtInValidators[$type];
+ if (isset(static::$builtInValidators[$type])) {
+ $type = static::$builtInValidators[$type];
}
if (is_array($type)) {
foreach ($type as $name => $value) {
@@ -163,6 +157,17 @@ abstract class Validator extends Component
}
/**
+ * @inheritdoc
+ */
+ public function init()
+ {
+ parent::init();
+ $this->attributes = (array)$this->attributes;
+ $this->on = (array)$this->on;
+ $this->except = (array)$this->except;
+ }
+
+ /**
* Validates the specified object.
* @param \yii\base\Model $object the data object being validated
* @param array|null $attributes the list of attributes to be validated.
@@ -170,7 +175,7 @@ abstract class Validator extends Component
* it will be ignored.
* If this parameter is null, every attribute listed in [[attributes]] will be validated.
*/
- public function validate($object, $attributes = null)
+ public function validateAttributes($object, $attributes = null)
{
if (is_array($attributes)) {
$attributes = array_intersect($this->attributes, $attributes);
@@ -187,12 +192,49 @@ abstract class Validator extends Component
}
/**
+ * Validates a single attribute.
+ * Child classes must implement this method to provide the actual validation logic.
+ * @param \yii\base\Model $object the data object to be validated
+ * @param string $attribute the name of the attribute to be validated.
+ */
+ public function validateAttribute($object, $attribute)
+ {
+ $result = $this->validateValue($object->$attribute);
+ if (!empty($result)) {
+ $this->addError($object, $attribute, $result[0], $result[1]);
+ }
+ }
+
+ /**
+ * Validates a given value.
+ * You may use this method to validate a value out of the context of a data model.
+ * @param mixed $value the data value to be validated.
+ * @param string $error the error message to be returned, if the validation fails.
+ * @return boolean whether the data is valid.
+ */
+ public function validate($value, &$error = null)
+ {
+ $result = $this->validateValue($value);
+ if (empty($result)) {
+ return true;
+ } else {
+ list($message, $params) = $result;
+ $params['attribute'] = Yii::t('yii', 'the input value');
+ $params['value'] = is_array($value) ? 'array()' : $value;
+ $error = Yii::$app->getI18n()->format($message, $params, Yii::$app->language);
+ return false;
+ }
+ }
+
+ /**
* Validates a value.
* A validator class can implement this method to support data validation out of the context of a data model.
* @param mixed $value the data value to be validated.
- * @throws NotSupportedException if data validation without a model is not supported
+ * @return array|null the error message and the parameters to be inserted into the error message.
+ * Null should be returned if the data is valid.
+ * @throws NotSupportedException if the validator does not supporting data validation without a model
*/
- public function validateValue($value)
+ protected function validateValue($value)
{
throw new NotSupportedException(get_class($this) . ' does not support validateValue().');
}
@@ -211,7 +253,7 @@ abstract class Validator extends Component
*
* @param \yii\base\Model $object the data object being validated
* @param string $attribute the name of the attribute to be validated.
- * @param \yii\base\View $view the view object that is going to be used to render views or view files
+ * @param \yii\web\View $view the view object that is going to be used to render views or view files
* containing a model form with this validator applied.
* @return string the client-side validation script. Null if the validator does not support
* client-side validation.
@@ -246,12 +288,12 @@ abstract class Validator extends Component
* @param string $message the error message
* @param array $params values for the placeholders in the error message
*/
- public function addError($object, $attribute, $message, $params = array())
+ public function addError($object, $attribute, $message, $params = [])
{
$value = $object->$attribute;
- $params['{attribute}'] = $object->getAttributeLabel($attribute);
- $params['{value}'] = is_array($value) ? 'array()' : $value;
- $object->addError($attribute, strtr($message, $params));
+ $params['attribute'] = $object->getAttributeLabel($attribute);
+ $params['value'] = is_array($value) ? 'array()' : $value;
+ $object->addError($attribute, Yii::$app->getI18n()->format($message, $params, Yii::$app->language));
}
/**
@@ -264,7 +306,7 @@ abstract class Validator extends Component
*/
public function isEmpty($value, $trim = false)
{
- return $value === null || $value === array() || $value === ''
+ return $value === null || $value === [] || $value === ''
|| $trim && is_scalar($value) && trim($value) === '';
}
}
diff --git a/framework/yii/views/errorHandler/callStackItem.php b/framework/yii/views/errorHandler/callStackItem.php
index 7514119..566e1d3 100644
--- a/framework/yii/views/errorHandler/callStackItem.php
+++ b/framework/yii/views/errorHandler/callStackItem.php
@@ -1,6 +1,5 @@
context;
?>
-
+
-
.
-
htmlEncode($file); ?>
+
= (int)$index ?>.
+
htmlEncode($file); ?>
- addTypeLinks($class) . '→'; ?>addTypeLinks($method . '()'); ?>
+ addTypeLinks($class) . '::'; ?>= $handler->addTypeLinks($method . '()') ?>
@@ -34,11 +32,11 @@ $context = $this->context;
-
+
= (int)($i + 1) ?>
htmlEncode($lines[$i]);
+ echo (trim($lines[$i]) == '') ? " \n" : $handler->htmlEncode($lines[$i]);
}
?>
diff --git a/framework/yii/views/errorHandler/error.php b/framework/yii/views/errorHandler/error.php
new file mode 100644
index 0000000..066d7e4
--- /dev/null
+++ b/framework/yii/views/errorHandler/error.php
@@ -0,0 +1,86 @@
+statusCode;
+} else {
+ $code = $exception->getCode();
+}
+if ($exception instanceof \yii\base\Exception) {
+ $name = $exception->getName();
+} else {
+ $name = 'Error';
+}
+if ($code) {
+ $name .= " (#$code)";
+}
+
+if ($exception instanceof \yii\base\UserException) {
+ $message = $exception->getMessage();
+} else {
+ $message = 'An internal server error occurred.';
+}
+?>
+beginPage(); ?>
+
+
+
+
+
= $handler->htmlEncode($name) ?>
+
+
+
+
+
+
= $handler->htmlEncode($name) ?>
+
= nl2br($handler->htmlEncode($message)) ?>
+
+ The above error occurred while the Web server was processing your request.
+
+
+ Please contact us if you think this is a server error. Thank you.
+
+
+ = date('Y-m-d H:i:s', time()) ?>
+
+ endBody(); // to allow injecting code into body (mostly by Yii Debug Toolbar) ?>
+
+
+endPage(); ?>
diff --git a/framework/yii/views/errorHandler/exception.php b/framework/yii/views/errorHandler/exception.php
new file mode 100644
index 0000000..1e37401
--- /dev/null
+++ b/framework/yii/views/errorHandler/exception.php
@@ -0,0 +1,483 @@
+
+beginPage(); ?>
+
+
+
+
+
+
+
statusCode . ' ' . $handler->htmlEncode($exception->getName());
+ } elseif ($exception instanceof \yii\base\Exception) {
+ echo $handler->htmlEncode($exception->getName() . ' – ' . get_class($exception));
+ } else {
+ echo $handler->htmlEncode(get_class($exception));
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+ = $handler->renderCallStackItem($exception->getFile(), $exception->getLine(), null, null, 1) ?>
+ getTrace(), $length = count($trace); $i < $length; ++$i): ?>
+ = $handler->renderCallStackItem(@$trace[$i]['file'] ?: null, @$trace[$i]['line'] ?: null,
+ @$trace[$i]['class'] ?: null, @$trace[$i]['function'] ?: null, $i + 2) ?>
+
+
+
+
+
+
+ = $handler->renderRequest() ?>
+
+
+
+
+
+
+
+
+
+
+ endBody(); // to allow injecting code into body (mostly by Yii Debug Toolbar) ?>
+
+
+
+endPage(); ?>
diff --git a/framework/yii/views/errorHandler/main.php b/framework/yii/views/errorHandler/main.php
deleted file mode 100644
index d7bbb3d..0000000
--- a/framework/yii/views/errorHandler/main.php
+++ /dev/null
@@ -1,492 +0,0 @@
-context;
-?>
-
-
-
-
-
-
-
statusCode . ' ' . $context->htmlEncode($exception->getName());
- } elseif ($exception instanceof \yii\base\Exception) {
- echo $context->htmlEncode($exception->getName() . ' – ' . get_class($exception));
- } else {
- echo $context->htmlEncode(get_class($exception));
- }
- ?>
-
-
-
-
-
-
-
-
-
- renderCallStackItem($exception->getFile(), $exception->getLine(), null, null, 1); ?>
- getTrace(), $length = count($trace); $i < $length; ++$i): ?>
- renderCallStackItem(@$trace[$i]['file'] ?: null, @$trace[$i]['line'] ?: null,
- @$trace[$i]['class'] ?: null, @$trace[$i]['function'] ?: null, $i + 1); ?>
-
-
-
-
-
-
-
htmlEncode($request); ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/framework/yii/views/errorHandler/previousException.php b/framework/yii/views/errorHandler/previousException.php
index 1cdf3c5..766c0a7 100644
--- a/framework/yii/views/errorHandler/previousException.php
+++ b/framework/yii/views/errorHandler/previousException.php
@@ -1,24 +1,21 @@
context;
?>
↵
Caused by:
- htmlEncode($exception->getName()); ?> –
- addTypeLinks(get_class($exception)); ?>
+ = $handler->htmlEncode($exception->getName()) ?> –
+ = $handler->addTypeLinks(get_class($exception)) ?>
- htmlEncode(get_class($exception)); ?>
+ = $handler->htmlEncode(get_class($exception)) ?>
-
htmlEncode($exception->getMessage()); ?>
-
in getFile(); ?> at line getLine(); ?>
-
+
= nl2br($handler->htmlEncode($exception->getMessage())) ?>
+
in = $exception->getFile() ?> at line = $exception->getLine() ?>
+ = $handler->renderPreviousExceptions($exception) ?>
diff --git a/framework/yii/views/messageConfig.php b/framework/yii/views/messageConfig.php
new file mode 100644
index 0000000..9babb0c
--- /dev/null
+++ b/framework/yii/views/messageConfig.php
@@ -0,0 +1,45 @@
+ __DIR__,
+ // string, required, root directory containing message translations.
+ 'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages',
+ // array, required, list of language codes that the extracted messages
+ // should be translated to. For example, ['zh_cn', 'de'].
+ 'languages' => ['de'],
+ // string, the name of the function for translating messages.
+ // Defaults to 'Yii::t'. This is used as a mark to find the messages to be
+ // translated. You may use a string for single function name or an array for
+ // multiple function names.
+ 'translator' => 'Yii::t',
+ // boolean, whether to sort messages by keys when merging new messages
+ // with the existing ones. Defaults to false, which means the new (untranslated)
+ // messages will be separated from the old (translated) ones.
+ 'sort' => false,
+ // boolean, whether the message file should be overwritten with the merged messages
+ 'overwrite' => true,
+ // boolean, whether to remove messages that no longer appear in the source code.
+ // Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks.
+ 'removeUnused' => false,
+ // array, list of patterns that specify which files/directories should be processed.
+ // If empty or not set, all files/directories will be processed.
+ // A path matches a pattern if it contains the pattern string at its end. For example,
+ // '/a/b' will match all files and directories ending with '/a/b';
+ // and the '.svn' will match all files and directories whose name ends with '.svn'.
+ // Note, the '/' characters in a pattern matches both '/' and '\'.
+ // If a file/directory matches both a pattern in "only" and "except", it will NOT be processed.
+ 'only' => ['.php'],
+ // array, list of patterns that specify which files/directories should NOT be processed.
+ // If empty or not set, all files/directories will be processed.
+ // Please refer to "only" for details about the patterns.
+ 'except' => [
+ '.svn',
+ '.git',
+ '.gitignore',
+ '.gitkeep',
+ '.hgignore',
+ '.hgkeep',
+ '/messages',
+ ],
+];
diff --git a/framework/yii/views/migration.php b/framework/yii/views/migration.php
index a75e22f..f6add4d 100644
--- a/framework/yii/views/migration.php
+++ b/framework/yii/views/migration.php
@@ -8,7 +8,9 @@
echo "
-class extends \yii\db\Migration
+use yii\db\Schema;
+
+class = $className ?> extends \yii\db\Migration
{
public function up()
{
@@ -17,7 +19,7 @@ class extends \yii\db\Migration
public function down()
{
- echo " cannot be reverted.\n";
+ echo "= $className ?> cannot be reverted.\n";
return false;
}
}
diff --git a/framework/yii/web/AccessControl.php b/framework/yii/web/AccessControl.php
index ce64533..e755e80 100644
--- a/framework/yii/web/AccessControl.php
+++ b/framework/yii/web/AccessControl.php
@@ -10,7 +10,6 @@ namespace yii\web;
use Yii;
use yii\base\Action;
use yii\base\ActionFilter;
-use yii\base\HttpException;
/**
* AccessControl provides simple access control based on a set of rules.
@@ -18,7 +17,7 @@ use yii\base\HttpException;
* AccessControl is an action filter. It will check its [[rules]] to find
* the first rule that matches the current context variables (such as user IP address, user role).
* The matching rule will dictate whether to allow or deny the access to the requested controller
- * action.
+ * action. If no rule matches, the access will be denied.
*
* To use AccessControl, declare it in the `behaviors()` method of your controller class.
* For example, the following declarations will allow authenticated users to access the "create"
@@ -27,23 +26,25 @@ use yii\base\HttpException;
* ~~~
* public function behaviors()
* {
- * return array(
- * 'access' => array(
+ * return [
+ * 'access' => [
* 'class' => \yii\web\AccessControl::className(),
- * 'only' => array('create', 'update'),
- * 'rules' => array(
+ * 'only' => ['create', 'update'],
+ * 'rules' => [
+ * // deny all POST requests
+ * [
+ * 'allow' => false,
+ * 'verbs' => ['POST']
+ * ],
* // allow authenticated users
- * array(
+ * [
* 'allow' => true,
- * 'roles' => array('@'),
- * ),
- * // deny all
- * array(
- * 'allow' => false,
- * ),
- * ),
- * ),
- * );
+ * 'roles' => ['@'],
+ * ],
+ * // everything else is denied
+ * ],
+ * ],
+ * ];
* }
* ~~~
*
@@ -69,16 +70,14 @@ class AccessControl extends ActionFilter
* @var array the default configuration of access rules. Individual rule configurations
* specified via [[rules]] will take precedence when the same property of the rule is configured.
*/
- public $ruleConfig = array(
- 'class' => 'yii\web\AccessRule',
- );
+ public $ruleConfig = ['class' => 'yii\web\AccessRule'];
/**
* @var array a list of access rule objects or configuration arrays for creating the rule objects.
* If a rule is specified via a configuration array, it will be merged with [[ruleConfig]] first
* before it is used for creating the rule object.
* @see ruleConfig
*/
- public $rules = array();
+ public $rules = [];
/**
* Initializes the [[rules]] array by instantiating rule objects from configurations.
@@ -103,10 +102,10 @@ class AccessControl extends ActionFilter
{
$user = Yii::$app->getUser();
$request = Yii::$app->getRequest();
- /** @var $rule AccessRule */
+ /** @var AccessRule $rule */
foreach ($this->rules as $rule) {
if ($allow = $rule->allows($action, $user, $request)) {
- break;
+ return true;
} elseif ($allow === false) {
if (isset($rule->denyCallback)) {
call_user_func($rule->denyCallback, $rule);
@@ -118,7 +117,13 @@ class AccessControl extends ActionFilter
return false;
}
}
- return true;
+ if (isset($this->denyCallback)) {
+ call_user_func($this->denyCallback, $rule);
+ }
+ else {
+ $this->denyAccess($user);
+ }
+ return false;
}
/**
@@ -126,14 +131,14 @@ class AccessControl extends ActionFilter
* The default implementation will redirect the user to the login page if he is a guest;
* if the user is already logged, a 403 HTTP exception will be thrown.
* @param User $user the current user
- * @throws HttpException if the user is already logged in.
+ * @throws AccessDeniedHttpException if the user is already logged in.
*/
protected function denyAccess($user)
{
if ($user->getIsGuest()) {
$user->loginRequired();
} else {
- throw new HttpException(403, Yii::t('yii', 'You are not allowed to perform this action.'));
+ throw new AccessDeniedHttpException(Yii::t('yii', 'You are not allowed to perform this action.'));
}
}
}
diff --git a/framework/yii/web/AccessDeniedHttpException.php b/framework/yii/web/AccessDeniedHttpException.php
new file mode 100644
index 0000000..d83700b
--- /dev/null
+++ b/framework/yii/web/AccessDeniedHttpException.php
@@ -0,0 +1,28 @@
+
+ * @since 2.0
+ */
+class AccessDeniedHttpException extends HttpException
+{
+ /**
+ * Constructor.
+ * @param string $message error message
+ * @param integer $code error code
+ * @param \Exception $previous The previous exception used for the exception chaining.
+ */
+ public function __construct($message = null, $code = 0, \Exception $previous = null)
+ {
+ parent::__construct(403, $message, $code, $previous);
+ }
+}
diff --git a/framework/yii/web/AccessRule.php b/framework/yii/web/AccessRule.php
index 9bd52ce..7aeaac1 100644
--- a/framework/yii/web/AccessRule.php
+++ b/framework/yii/web/AccessRule.php
@@ -11,6 +11,7 @@ use yii\base\Component;
use yii\base\Action;
/**
+ * This class represents an access rule defined by the [[AccessControl]] action filter
*
* @author Qiang Xue
* @since 2.0
@@ -39,7 +40,7 @@ class AccessRule extends Component
* - `@`: matches an authenticated user
*
* Using additional role names requires RBAC (Role-Based Access Control), and
- * [[User::hasAccess()]] will be called.
+ * [[User::checkAccess()]] will be called.
*
* If this property is not set or empty, it means this rule applies to all roles.
*/
diff --git a/framework/yii/web/Application.php b/framework/yii/web/Application.php
index 12c9295..17c1411 100644
--- a/framework/yii/web/Application.php
+++ b/framework/yii/web/Application.php
@@ -8,11 +8,17 @@
namespace yii\web;
use Yii;
-use yii\base\HttpException;
use yii\base\InvalidRouteException;
/**
- * Application is the base class for all application classes.
+ * Application is the base class for all web application classes.
+ *
+ * @property AssetManager $assetManager The asset manager component. This property is read-only.
+ * @property string $homeUrl The homepage URL.
+ * @property Request $request The request component. This property is read-only.
+ * @property Response $response The response component. This property is read-only.
+ * @property Session $session The session component. This property is read-only.
+ * @property User $user The user component. This property is read-only.
*
* @author Qiang Xue
* @since 2.0
@@ -32,28 +38,33 @@ class Application extends \yii\base\Application
* to the action. For example,
*
* ~~~
- * array(
+ * [
* 'offline/notice',
* 'param1' => 'value1',
* 'param2' => 'value2',
- * )
+ * ]
* ~~~
*
- * Defaults to null, meaning catch-all is not effective.
+ * Defaults to null, meaning catch-all is not used.
*/
public $catchAll;
+ /**
+ * @var Controller the currently active controller instance
+ */
+ public $controller;
/**
- * Processes the request.
- * @return integer the exit status of the controller action (0 means normal, non-zero values mean abnormal)
- * @throws HttpException if the request cannot be resolved.
+ * Handles the specified request.
+ * @param Request $request the request to be handled
+ * @return Response the resulting response
+ * @throws NotFoundHttpException if the requested route is invalid
*/
- public function processRequest()
+ public function handleRequest($request)
{
- $request = $this->getRequest();
- Yii::setAlias('@wwwroot', dirname($request->getScriptFile()));
- Yii::setAlias('@www', $request->getBaseUrl());
+ Yii::setAlias('@webroot', dirname($request->getScriptFile()));
+ Yii::setAlias('@web', $request->getBaseUrl());
+
if (empty($this->catchAll)) {
list ($route, $params) = $request->resolve();
} else {
@@ -61,9 +72,20 @@ class Application extends \yii\base\Application
$params = array_splice($this->catchAll, 1);
}
try {
- return $this->runAction($route, $params);
+ Yii::trace("Route requested: '$route'", __METHOD__);
+ $this->requestedRoute = $route;
+ $result = $this->runAction($route, $params);
+ if ($result instanceof Response) {
+ return $result;
+ } else {
+ $response = $this->getResponse();
+ if ($result !== null) {
+ $response->data = $result;
+ }
+ return $response;
+ }
} catch (InvalidRouteException $e) {
- throw new HttpException(404, $e->getMessage(), $e->getCode(), $e);
+ throw new NotFoundHttpException($e->getMessage(), $e->getCode(), $e);
}
}
@@ -145,22 +167,12 @@ class Application extends \yii\base\Application
public function registerCoreComponents()
{
parent::registerCoreComponents();
- $this->setComponents(array(
- 'request' => array(
- 'class' => 'yii\web\Request',
- ),
- 'response' => array(
- 'class' => 'yii\web\Response',
- ),
- 'session' => array(
- 'class' => 'yii\web\Session',
- ),
- 'user' => array(
- 'class' => 'yii\web\User',
- ),
- 'assetManager' => array(
- 'class' => 'yii\web\AssetManager',
- ),
- ));
+ $this->setComponents([
+ 'request' => ['class' => 'yii\web\Request'],
+ 'response' => ['class' => 'yii\web\Response'],
+ 'session' => ['class' => 'yii\web\Session'],
+ 'user' => ['class' => 'yii\web\User'],
+ 'assetManager' => ['class' => 'yii\web\AssetManager'],
+ ]);
}
}
diff --git a/framework/yii/web/AssetBundle.php b/framework/yii/web/AssetBundle.php
index daa533f..964fe98 100644
--- a/framework/yii/web/AssetBundle.php
+++ b/framework/yii/web/AssetBundle.php
@@ -10,12 +10,14 @@ namespace yii\web;
use Yii;
use yii\base\InvalidConfigException;
use yii\base\Object;
+use yii\web\View;
/**
* AssetBundle represents a collection of asset files, such as CSS, JS, images.
*
- * Each asset bundle has a unique name that globally identifies it among all asset bundles
- * used in an application.
+ * Each asset bundle has a unique name that globally identifies it among all asset bundles used in an application.
+ * The name is the [fully qualified class name](http://php.net/manual/en/language.namespaces.rules.php)
+ * of the class representing it.
*
* An asset bundle can depend on other asset bundles. When registering an asset bundle
* with a view, all its dependent asset bundles will be automatically registered.
@@ -45,7 +47,7 @@ class AssetBundle extends Object
* when it publishes the asset files from [[sourcePath]].
*
* If the bundle contains any assets that are specified in terms of relative file path,
- * then this property must be set either manually or automatically (by asset manager via
+ * then this property must be set either manually or automatically (by [[AssetManager]] via
* asset publishing).
*
* You can use either a directory or an alias of the directory.
@@ -66,9 +68,18 @@ class AssetBundle extends Object
*/
public $baseUrl;
/**
- * @var array list of the bundle names that this bundle depends on
+ * @var array list of bundle class names that this bundle depends on.
+ *
+ * For example:
+ *
+ * ```php
+ * public $depends = [
+ * 'yii\web\YiiAsset',
+ * 'yii\bootstrap\BootstrapAsset',
+ * ];
+ * ```
*/
- public $depends = array();
+ public $depends = [];
/**
* @var array list of JavaScript files that this bundle contains. Each JavaScript file can
* be either a file path (without leading slash) relative to [[basePath]] or a URL representing
@@ -76,7 +87,7 @@ class AssetBundle extends Object
*
* Note that only forward slash "/" can be used as directory separator.
*/
- public $js = array();
+ public $js = [];
/**
* @var array list of CSS files that this bundle contains. Each CSS file can
* be either a file path (without leading slash) relative to [[basePath]] or a URL representing
@@ -84,25 +95,35 @@ class AssetBundle extends Object
*
* Note that only forward slash "/" can be used as directory separator.
*/
- public $css = array();
+ public $css = [];
/**
- * @var array the options that will be passed to [[\yii\base\View::registerJsFile()]]
+ * @var array the options that will be passed to [[\yii\web\View::registerJsFile()]]
* when registering the JS files in this bundle.
*/
- public $jsOptions = array();
+ public $jsOptions = [];
/**
- * @var array the options that will be passed to [[\yii\base\View::registerCssFile()]]
+ * @var array the options that will be passed to [[\yii\web\View::registerCssFile()]]
* when registering the CSS files in this bundle.
*/
- public $cssOptions = array();
+ public $cssOptions = [];
/**
* @var array the options to be passed to [[AssetManager::publish()]] when the asset bundle
* is being published.
*/
- public $publishOptions = array();
+ public $publishOptions = [];
+
+ /**
+ * @param View $view
+ * @return AssetBundle the registered asset bundle instance
+ */
+ public static function register($view)
+ {
+ return $view->registerAssetBundle(get_called_class());
+ }
/**
* Initializes the bundle.
+ * If you override this method, make sure you call the parent implementation in the last.
*/
public function init()
{
@@ -119,38 +140,37 @@ class AssetBundle extends Object
/**
* Registers the CSS and JS files with the given view.
- * This method will first register all dependent asset bundles.
- * It will then try to convert non-CSS or JS files (e.g. LESS, Sass) into the corresponding
- * CSS or JS files using [[AssetManager::converter|asset converter]].
- * @param \yii\base\View $view the view that the asset files to be registered with.
- * @throws InvalidConfigException if [[baseUrl]] or [[basePath]] is not set when the bundle
- * contains internal CSS or JS files.
+ * @param \yii\web\View $view the view that the asset files are to be registered with.
*/
- public function registerAssets($view)
+ public function registerAssetFiles($view)
{
- foreach ($this->depends as $name) {
- $view->registerAssetBundle($name);
- }
-
- $this->publish($view->getAssetManager());
-
foreach ($this->js as $js) {
- $view->registerJsFile($this->baseUrl . '/' . $js, $this->jsOptions);
+ if (strpos($js, '/') !== 0 && strpos($js, '://') === false) {
+ $view->registerJsFile($this->baseUrl . '/' . $js, $this->jsOptions);
+ } else {
+ $view->registerJsFile($js, $this->jsOptions);
+ }
}
foreach ($this->css as $css) {
- $view->registerCssFile($this->baseUrl . '/' . $css, $this->cssOptions);
+ if (strpos($css, '/') !== 0 && strpos($css, '://') === false) {
+ $view->registerCssFile($this->baseUrl . '/' . $css, $this->cssOptions);
+ } else {
+ $view->registerCssFile($css, $this->cssOptions);
+ }
}
}
/**
* Publishes the asset bundle if its source code is not under Web-accessible directory.
+ * It will also try to convert non-CSS or JS files (e.g. LESS, Sass) into the corresponding
+ * CSS or JS files using [[AssetManager::converter|asset converter]].
* @param AssetManager $am the asset manager to perform the asset publishing
* @throws InvalidConfigException if [[baseUrl]] or [[basePath]] is not set when the bundle
* contains internal CSS or JS files.
*/
public function publish($am)
{
- if ($this->sourcePath !== null) {
+ if ($this->sourcePath !== null && !isset($this->basePath, $this->baseUrl)) {
list ($this->basePath, $this->baseUrl) = $am->publish($this->sourcePath, $this->publishOptions);
}
$converter = $am->getConverter();
diff --git a/framework/yii/web/AssetConverter.php b/framework/yii/web/AssetConverter.php
index cfad360..1b7d1c8 100644
--- a/framework/yii/web/AssetConverter.php
+++ b/framework/yii/web/AssetConverter.php
@@ -9,26 +9,31 @@ namespace yii\web;
use Yii;
use yii\base\Component;
+use yii\base\Exception;
/**
* AssetConverter supports conversion of several popular script formats into JS or CSS scripts.
*
+ * It is used by [[AssetManager]] to convert files after they have been published.
+ *
* @author Qiang Xue
* @since 2.0
*/
-class AssetConverter extends Component implements IAssetConverter
+class AssetConverter extends Component implements AssetConverterInterface
{
/**
* @var array the commands that are used to perform the asset conversion.
* The keys are the asset file extension names, and the values are the corresponding
* target script types (either "css" or "js") and the commands used for the conversion.
*/
- public $commands = array(
- 'less' => array('css', 'lessc {from} {to}'),
- 'scss' => array('css', 'sass {from} {to}'),
- 'sass' => array('css', 'sass {from} {to}'),
- 'styl' => array('js', 'stylus < {from} > {to}'),
- );
+ public $commands = [
+ 'less' => ['css', 'lessc {from} {to} --no-color'],
+ 'scss' => ['css', 'sass {from} {to}'],
+ 'sass' => ['css', 'sass {from} {to}'],
+ 'styl' => ['js', 'stylus < {from} > {to}'],
+ 'coffee' => ['js', 'coffee -p {from} > {to}'],
+ 'ts' => ['js', 'tsc --out {to} {from}'],
+ ];
/**
* Converts a given asset file into a CSS or JS file.
@@ -39,23 +44,56 @@ class AssetConverter extends Component implements IAssetConverter
public function convert($asset, $basePath)
{
$pos = strrpos($asset, '.');
- if ($pos === false) {
+ if ($pos !== false) {
$ext = substr($asset, $pos + 1);
if (isset($this->commands[$ext])) {
list ($ext, $command) = $this->commands[$ext];
$result = substr($asset, 0, $pos + 1) . $ext;
if (@filemtime("$basePath/$result") < filemtime("$basePath/$asset")) {
- $output = array();
- $command = strtr($command, array(
- '{from}' => "$basePath/$asset",
- '{to}' => "$basePath/$result",
- ));
- exec($command, $output);
- Yii::info("Converted $asset into $result: " . implode("\n", $output), __METHOD__);
+ $this->runCommand($command, $basePath, $asset, $result);
}
return $result;
}
}
return $asset;
}
+
+ /**
+ * Runs a command to convert asset files.
+ * @param string $command the command to run
+ * @param string $basePath asset base path and command working directory
+ * @param string $asset the name of the asset file
+ * @param string $result the name of the file to be generated by the converter command
+ * @return bool true on success, false on failure. Failures will be logged.
+ * @throws \yii\base\Exception when the command fails and YII_DEBUG is true.
+ * In production mode the error will be logged.
+ */
+ protected function runCommand($command, $basePath, $asset, $result)
+ {
+ $command = strtr($command, [
+ '{from}' => escapeshellarg("$basePath/$asset"),
+ '{to}' => escapeshellarg("$basePath/$result"),
+ ]);
+ $descriptor = [
+ 1 => ['pipe', 'w'],
+ 2 => ['pipe', 'w'],
+ ];
+ $pipes = [];
+ $proc = proc_open($command, $descriptor, $pipes, $basePath);
+ $stdout = stream_get_contents($pipes[1]);
+ $stderr = stream_get_contents($pipes[2]);
+ foreach($pipes as $pipe) {
+ fclose($pipe);
+ }
+ $status = proc_close($proc);
+
+ if ($status === 0) {
+ Yii::trace("Converted $asset into $result:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr", __METHOD__);
+ } elseif (YII_DEBUG) {
+ throw new Exception("AssetConverter command '$command' failed with exit code $status:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr");
+ } else {
+ Yii::error("AssetConverter command '$command' failed with exit code $status:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr");
+ }
+ return $status === 0;
+ }
}
diff --git a/framework/yii/web/AssetConverterInterface.php b/framework/yii/web/AssetConverterInterface.php
new file mode 100644
index 0000000..51309c6
--- /dev/null
+++ b/framework/yii/web/AssetConverterInterface.php
@@ -0,0 +1,25 @@
+
+ * @since 2.0
+ */
+interface AssetConverterInterface
+{
+ /**
+ * Converts a given asset file into a CSS or JS file.
+ * @param string $asset the asset file path, relative to $basePath
+ * @param string $basePath the directory the $asset is relative to.
+ * @return string the converted asset file path, relative to $basePath.
+ */
+ public function convert($asset, $basePath);
+}
diff --git a/framework/yii/web/AssetManager.php b/framework/yii/web/AssetManager.php
index dfec576..89e746e 100644
--- a/framework/yii/web/AssetManager.php
+++ b/framework/yii/web/AssetManager.php
@@ -16,24 +16,53 @@ use yii\helpers\FileHelper;
/**
* AssetManager manages asset bundles and asset publishing.
*
+ * AssetManager is configured as an application component in [[yii\web\Application]] by default.
+ * You can access that instance via `Yii::$app->assetManager`.
+ *
+ * You can modify its configuration by adding an array to your application config under `components`
+ * as it is shown in the following example:
+ *
+ * ~~~
+ * 'assetManager' => [
+ * 'bundles' => [
+ * // you can override AssetBundle configs here
+ * ],
+ * //'linkAssets' => true,
+ * // ...
+ * ]
+ * ~~~
+ *
+ * @property AssetConverterInterface $converter The asset converter. Note that the type of this property
+ * differs in getter and setter. See [[getConverter()]] and [[setConverter()]] for details.
+ *
* @author Qiang Xue
* @since 2.0
*/
class AssetManager extends Component
{
/**
- * @var array list of available asset bundles. The keys are the bundle names, and the values are the configuration
- * arrays for creating the [[AssetBundle]] objects.
+ * @var array list of available asset bundles. The keys are the class names (without leading backslash)
+ * of the asset bundles, and the values are either the configuration arrays for creating the [[AssetBundle]]
+ * objects or the corresponding asset bundle instances. For example, the following code disables
+ * the bootstrap css file used by Bootstrap widgets (because you want to use your own styles):
+ *
+ * ~~~
+ * [
+ * 'yii\bootstrap\BootstrapAsset' => [
+ * 'css' => [],
+ * ],
+ * ]
+ * ~~~
*/
- public $bundles;
+ public $bundles = [];
/**
* @return string the root directory storing the published asset files.
*/
- public $basePath = '@wwwroot/assets';
+ public $basePath = '@webroot/assets';
/**
* @return string the base URL through which the published asset files can be accessed.
*/
- public $baseUrl = '@www/assets';
+ public $baseUrl = '@web/assets';
/**
* @var boolean whether to use symbolic link to publish asset files. Defaults to false, meaning
* asset files are copied to [[basePath]]. Using symbolic links has the benefit that the published
@@ -54,16 +83,17 @@ class AssetManager extends Component
public $linkAssets = false;
/**
* @var integer the permission to be set for newly published asset files.
- * This value will be used by PHP chmod() function.
+ * This value will be used by PHP chmod() function. No umask will be applied.
* If not set, the permission will be determined by the current environment.
*/
public $fileMode;
/**
* @var integer the permission to be set for newly generated asset directories.
- * This value will be used by PHP chmod() function.
- * Defaults to 0777, meaning the directory can be read, written and executed by all users.
+ * This value will be used by PHP chmod() function. No umask will be applied.
+ * Defaults to 0775, meaning the directory is read-writable by owner and group,
+ * but read-only for other users.
*/
- public $dirMode = 0777;
+ public $dirMode = 0775;
/**
* Initializes the component.
@@ -81,70 +111,50 @@ class AssetManager extends Component
$this->basePath = realpath($this->basePath);
}
$this->baseUrl = rtrim(Yii::getAlias($this->baseUrl), '/');
-
- foreach (require(YII_PATH . '/assets.php') as $name => $bundle) {
- if (!isset($this->bundles[$name])) {
- $this->bundles[$name] = $bundle;
- }
- }
}
/**
- * Returns the named bundle.
- * This method will first look for the bundle in [[bundles]]. If not found,
- * it will attempt to find the bundle from an installed extension using the following procedure:
- *
- * 1. Convert the bundle into a path alias;
- * 2. Determine the root alias and use it to locate the bundle manifest file "assets.php";
- * 3. Look for the bundle in the manifest file.
+ * Returns the named asset bundle.
*
- * For example, given the bundle name "foo/button", the method will first convert it
- * into the path alias "@foo/button"; since "@foo" is the root alias, it will look
- * for the bundle manifest file "@foo/assets.php". The manifest file should return an array
- * that lists the bundles used by the "foo/button" extension. The array format is the same as [[bundles]].
+ * This method will first look for the bundle in [[bundles]]. If not found,
+ * it will treat `$name` as the class of the asset bundle and create a new instance of it.
*
- * @param string $name the bundle name
- * @return AssetBundle the loaded bundle object. Null is returned if the bundle does not exist.
+ * @param string $name the class name of the asset bundle
+ * @param boolean $publish whether to publish the asset files in the asset bundle before it is returned.
+ * If you set this false, you must manually call `AssetBundle::publish()` to publish the asset files.
+ * @return AssetBundle the asset bundle instance
+ * @throws InvalidConfigException if $name does not refer to a valid asset bundle
*/
- public function getBundle($name)
+ public function getBundle($name, $publish = true)
{
- if (!isset($this->bundles[$name])) {
- $rootAlias = Yii::getRootAlias("@$name");
- if ($rootAlias !== false) {
- $manifest = Yii::getAlias("$rootAlias/assets.php", false);
- if ($manifest !== false && is_file($manifest)) {
- foreach (require($manifest) as $bn => $config) {
- $this->bundles[$bn] = $config;
- }
- }
- }
- if (!isset($this->bundles[$name])) {
- return null;
+ if (isset($this->bundles[$name])) {
+ if ($this->bundles[$name] instanceof AssetBundle) {
+ return $this->bundles[$name];
+ } elseif (is_array($this->bundles[$name])) {
+ $bundle = Yii::createObject(array_merge(['class' => $name], $this->bundles[$name]));
+ } else {
+ throw new InvalidConfigException("Invalid asset bundle: $name");
}
+ } else {
+ $bundle = Yii::createObject($name);
}
- if (is_array($this->bundles[$name])) {
- $config = $this->bundles[$name];
- if (!isset($config['class'])) {
- $config['class'] = 'yii\\web\\AssetBundle';
- $this->bundles[$name] = Yii::createObject($config);
- }
+ if ($publish) {
+ /** @var AssetBundle $bundle */
+ $bundle->publish($this);
}
-
- return $this->bundles[$name];
+ return $this->bundles[$name] = $bundle;
}
private $_converter;
/**
* Returns the asset converter.
- * @return IAssetConverter the asset converter.
+ * @return AssetConverterInterface the asset converter.
*/
public function getConverter()
{
if ($this->_converter === null) {
- $this->_converter = Yii::createObject(array(
- 'class' => 'yii\\web\\AssetConverter',
- ));
+ $this->_converter = Yii::createObject(AssetConverter::className());
} elseif (is_array($this->_converter) || is_string($this->_converter)) {
$this->_converter = Yii::createObject($this->_converter);
}
@@ -153,8 +163,8 @@ class AssetManager extends Component
/**
* Sets the asset converter.
- * @param array|IAssetConverter $value the asset converter. This can be either
- * an object implementing the [[IAssetConverter]] interface, or a configuration
+ * @param array|AssetConverterInterface $value the asset converter. This can be either
+ * an object implementing the [[AssetConverterInterface]], or a configuration
* array that can be used to create the asset converter object.
*/
public function setConverter($value)
@@ -165,7 +175,7 @@ class AssetManager extends Component
/**
* @var array published assets
*/
- private $_published = array();
+ private $_published = [];
/**
* Publishes a file or a directory.
@@ -211,7 +221,7 @@ class AssetManager extends Component
* @return array the path (directory or file path) and the URL that the asset is published as.
* @throws InvalidParamException if the asset to be published does not exist.
*/
- public function publish($path, $options = array())
+ public function publish($path, $options = [])
{
if (isset($this->_published[$path])) {
return $this->_published[$path];
@@ -229,7 +239,7 @@ class AssetManager extends Component
$dstFile = $dstDir . DIRECTORY_SEPARATOR . $fileName;
if (!is_dir($dstDir)) {
- mkdir($dstDir, $this->dirMode, true);
+ FileHelper::createDirectory($dstDir, $this->dirMode, true);
}
if ($this->linkAssets) {
@@ -243,7 +253,7 @@ class AssetManager extends Component
}
}
- return $this->_published[$path] = array($dstFile, $this->baseUrl . "/$dir/$fileName");
+ return $this->_published[$path] = [$dstFile, $this->baseUrl . "/$dir/$fileName"];
} else {
$dir = $this->hash($src . filemtime($src));
$dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir;
@@ -252,10 +262,10 @@ class AssetManager extends Component
symlink($src, $dstDir);
}
} elseif (!is_dir($dstDir) || !empty($options['forceCopy'])) {
- $opts = array(
+ $opts = [
'dirMode' => $this->dirMode,
'fileMode' => $this->fileMode,
- );
+ ];
if (isset($options['beforeCopy'])) {
$opts['beforeCopy'] = $options['beforeCopy'];
} else {
@@ -269,7 +279,7 @@ class AssetManager extends Component
FileHelper::copyDirectory($src, $dstDir, $opts);
}
- return $this->_published[$path] = array($dstDir, $this->baseUrl . '/' . $dir);
+ return $this->_published[$path] = [$dstDir, $this->baseUrl . '/' . $dir];
}
}
diff --git a/framework/yii/web/BadRequestHttpException.php b/framework/yii/web/BadRequestHttpException.php
new file mode 100644
index 0000000..3a6cfbb
--- /dev/null
+++ b/framework/yii/web/BadRequestHttpException.php
@@ -0,0 +1,28 @@
+
+ * @since 2.0
+ */
+class BadRequestHttpException extends HttpException
+{
+ /**
+ * Constructor.
+ * @param string $message error message
+ * @param integer $code error code
+ * @param \Exception $previous The previous exception used for the exception chaining.
+ */
+ public function __construct($message = null, $code = 0, \Exception $previous = null)
+ {
+ parent::__construct(400, $message, $code, $previous);
+ }
+}
diff --git a/framework/yii/web/CacheSession.php b/framework/yii/web/CacheSession.php
index 79acd74..7b4a98d 100644
--- a/framework/yii/web/CacheSession.php
+++ b/framework/yii/web/CacheSession.php
@@ -19,7 +19,19 @@ use yii\base\InvalidConfigException;
*
* Beware, by definition cache storage are volatile, which means the data stored on them
* may be swapped out and get lost. Therefore, you must make sure the cache used by this component
- * is NOT volatile. If you want to use database as storage medium, use [[DbSession]] is a better choice.
+ * is NOT volatile. If you want to use database as storage medium, [[DbSession]] is a better choice.
+ *
+ * The following example shows how you can configure the application to use CacheSession:
+ * Add the following to your application config under `components`:
+ *
+ * ~~~
+ * 'session' => [
+ * 'class' => 'yii\web\CacheSession',
+ * // 'cache' => 'mycache',
+ * ]
+ * ~~~
+ *
+ * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
*
* @author Qiang Xue
* @since 2.0
@@ -40,13 +52,13 @@ class CacheSession extends Session
*/
public function init()
{
- parent::init();
if (is_string($this->cache)) {
$this->cache = Yii::$app->getComponent($this->cache);
}
if (!$this->cache instanceof Cache) {
throw new InvalidConfigException('CacheSession::cache must refer to the application component ID of a cache object.');
}
+ parent::init();
}
/**
@@ -101,6 +113,6 @@ class CacheSession extends Session
*/
protected function calculateKey($id)
{
- return array(__CLASS__, $id);
+ return [__CLASS__, $id];
}
}
diff --git a/framework/yii/web/CaptchaAction.php b/framework/yii/web/CaptchaAction.php
deleted file mode 100644
index 1ed1fb0..0000000
--- a/framework/yii/web/CaptchaAction.php
+++ /dev/null
@@ -1,336 +0,0 @@
-
- * @since 2.0
- */
-class CaptchaAction extends Action
-{
- /**
- * The name of the GET parameter indicating whether the CAPTCHA image should be regenerated.
- */
- const REFRESH_GET_VAR = 'refresh';
- /**
- * @var integer how many times should the same CAPTCHA be displayed. Defaults to 3.
- * A value less than or equal to 0 means the test is unlimited (available since version 1.1.2).
- */
- public $testLimit = 3;
- /**
- * @var integer the width of the generated CAPTCHA image. Defaults to 120.
- */
- public $width = 120;
- /**
- * @var integer the height of the generated CAPTCHA image. Defaults to 50.
- */
- public $height = 50;
- /**
- * @var integer padding around the text. Defaults to 2.
- */
- public $padding = 2;
- /**
- * @var integer the background color. For example, 0x55FF00.
- * Defaults to 0xFFFFFF, meaning white color.
- */
- public $backColor = 0xFFFFFF;
- /**
- * @var integer the font color. For example, 0x55FF00. Defaults to 0x2040A0 (blue color).
- */
- public $foreColor = 0x2040A0;
- /**
- * @var boolean whether to use transparent background. Defaults to false.
- */
- public $transparent = false;
- /**
- * @var integer the minimum length for randomly generated word. Defaults to 6.
- */
- public $minLength = 6;
- /**
- * @var integer the maximum length for randomly generated word. Defaults to 7.
- */
- public $maxLength = 7;
- /**
- * @var integer the offset between characters. Defaults to -2. You can adjust this property
- * in order to decrease or increase the readability of the captcha.
- **/
- public $offset = -2;
- /**
- * @var string the TrueType font file. This can be either a file path or path alias.
- */
- public $fontFile = '@yii/web/SpicyRice.ttf';
- /**
- * @var string the fixed verification code. When this is property is set,
- * [[getVerifyCode()]] will always return the value of this property.
- * This is mainly used in automated tests where we want to be able to reproduce
- * the same verification code each time we run the tests.
- * If not set, it means the verification code will be randomly generated.
- */
- public $fixedVerifyCode;
-
-
- /**
- * Initializes the action.
- * @throws InvalidConfigException if the font file does not exist.
- */
- public function init()
- {
- $this->fontFile = Yii::getAlias($this->fontFile);
- if (!is_file($this->fontFile)) {
- throw new InvalidConfigException("The font file does not exist: {$this->fontFile}");
- }
- }
-
- /**
- * Runs the action.
- */
- public function run()
- {
- if (isset($_GET[self::REFRESH_GET_VAR])) {
- // AJAX request for regenerating code
- $code = $this->getVerifyCode(true);
- echo json_encode(array(
- 'hash1' => $this->generateValidationHash($code),
- 'hash2' => $this->generateValidationHash(strtolower($code)),
- // we add a random 'v' parameter so that FireFox can refresh the image
- // when src attribute of image tag is changed
- 'url' => $this->controller->createUrl($this->id, array('v' => uniqid())),
- ));
- } else {
- $this->renderImage($this->getVerifyCode());
- }
- Yii::$app->end();
- }
-
- /**
- * Generates a hash code that can be used for client side validation.
- * @param string $code the CAPTCHA code
- * @return string a hash code generated from the CAPTCHA code
- */
- public function generateValidationHash($code)
- {
- for ($h = 0, $i = strlen($code) - 1; $i >= 0; --$i) {
- $h += ord($code[$i]);
- }
- return $h;
- }
-
- /**
- * Gets the verification code.
- * @param boolean $regenerate whether the verification code should be regenerated.
- * @return string the verification code.
- */
- public function getVerifyCode($regenerate = false)
- {
- if ($this->fixedVerifyCode !== null) {
- return $this->fixedVerifyCode;
- }
-
- $session = Yii::$app->session;
- $session->open();
- $name = $this->getSessionKey();
- if ($session[$name] === null || $regenerate) {
- $session[$name] = $this->generateVerifyCode();
- $session[$name . 'count'] = 1;
- }
- return $session[$name];
- }
-
- /**
- * Validates the input to see if it matches the generated code.
- * @param string $input user input
- * @param boolean $caseSensitive whether the comparison should be case-sensitive
- * @return boolean whether the input is valid
- */
- public function validate($input, $caseSensitive)
- {
- $code = $this->getVerifyCode();
- $valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;
- $session = Yii::$app->session;
- $session->open();
- $name = $this->getSessionKey() . 'count';
- $session[$name] = $session[$name] + 1;
- if ($session[$name] > $this->testLimit && $this->testLimit > 0) {
- $this->getVerifyCode(true);
- }
- return $valid;
- }
-
- /**
- * Generates a new verification code.
- * @return string the generated verification code
- */
- protected function generateVerifyCode()
- {
- if ($this->minLength < 3) {
- $this->minLength = 3;
- }
- if ($this->maxLength > 20) {
- $this->maxLength = 20;
- }
- if ($this->minLength > $this->maxLength) {
- $this->maxLength = $this->minLength;
- }
- $length = mt_rand($this->minLength, $this->maxLength);
-
- $letters = 'bcdfghjklmnpqrstvwxyz';
- $vowels = 'aeiou';
- $code = '';
- for ($i = 0; $i < $length; ++$i) {
- if ($i % 2 && mt_rand(0, 10) > 2 || !($i % 2) && mt_rand(0, 10) > 9) {
- $code .= $vowels[mt_rand(0, 4)];
- } else {
- $code .= $letters[mt_rand(0, 20)];
- }
- }
-
- return $code;
- }
-
- /**
- * Returns the session variable name used to store verification code.
- * @return string the session variable name
- */
- protected function getSessionKey()
- {
- return '__captcha/' . $this->getUniqueId();
- }
-
- /**
- * Renders the CAPTCHA image.
- * @param string $code the verification code
- */
- protected function renderImage($code)
- {
- if (Captcha::checkRequirements() === 'gd') {
- $this->renderImageByGD($code);
- } else {
- $this->renderImageByImagick($code);
- }
- }
-
- /**
- * Renders the CAPTCHA image based on the code using GD library.
- * @param string $code the verification code
- */
- protected function renderImageByGD($code)
- {
- $image = imagecreatetruecolor($this->width, $this->height);
-
- $backColor = imagecolorallocate($image,
- (int)($this->backColor % 0x1000000 / 0x10000),
- (int)($this->backColor % 0x10000 / 0x100),
- $this->backColor % 0x100);
- imagefilledrectangle($image, 0, 0, $this->width, $this->height, $backColor);
- imagecolordeallocate($image, $backColor);
-
- if ($this->transparent) {
- imagecolortransparent($image, $backColor);
- }
-
- $foreColor = imagecolorallocate($image,
- (int)($this->foreColor % 0x1000000 / 0x10000),
- (int)($this->foreColor % 0x10000 / 0x100),
- $this->foreColor % 0x100);
-
- $length = strlen($code);
- $box = imagettfbbox(30, 0, $this->fontFile, $code);
- $w = $box[4] - $box[0] + $this->offset * ($length - 1);
- $h = $box[1] - $box[5];
- $scale = min(($this->width - $this->padding * 2) / $w, ($this->height - $this->padding * 2) / $h);
- $x = 10;
- $y = round($this->height * 27 / 40);
- for ($i = 0; $i < $length; ++$i) {
- $fontSize = (int)(rand(26, 32) * $scale * 0.8);
- $angle = rand(-10, 10);
- $letter = $code[$i];
- $box = imagettftext($image, $fontSize, $angle, $x, $y, $foreColor, $this->fontFile, $letter);
- $x = $box[2] + $this->offset;
- }
-
- imagecolordeallocate($image, $foreColor);
-
- $this->sendHttpHeaders();
-
- imagepng($image);
- imagedestroy($image);
- }
-
- /**
- * Renders the CAPTCHA image based on the code using ImageMagick library.
- * @param string $code the verification code
- */
- protected function renderImageByImagick($code)
- {
- $backColor = $this->transparent ? new \ImagickPixel('transparent') : new \ImagickPixel('#' . dechex($this->backColor));
- $foreColor = new \ImagickPixel('#' . dechex($this->foreColor));
-
- $image = new \Imagick();
- $image->newImage($this->width, $this->height, $backColor);
-
- $draw = new \ImagickDraw();
- $draw->setFont($this->fontFile);
- $draw->setFontSize(30);
- $fontMetrics = $image->queryFontMetrics($draw, $code);
-
- $length = strlen($code);
- $w = (int)($fontMetrics['textWidth']) - 8 + $this->offset * ($length - 1);
- $h = (int)($fontMetrics['textHeight']) - 8;
- $scale = min(($this->width - $this->padding * 2) / $w, ($this->height - $this->padding * 2) / $h);
- $x = 10;
- $y = round($this->height * 27 / 40);
- for ($i = 0; $i < $length; ++$i) {
- $draw = new \ImagickDraw();
- $draw->setFont($this->fontFile);
- $draw->setFontSize((int)(rand(26, 32) * $scale * 0.8));
- $draw->setFillColor($foreColor);
- $image->annotateImage($draw, $x, $y, rand(-10, 10), $code[$i]);
- $fontMetrics = $image->queryFontMetrics($draw, $code[$i]);
- $x += (int)($fontMetrics['textWidth']) + $this->offset;
- }
-
- $image->setImageFormat('png');
- Yii::$app->getResponse()->content = (string)$image;
- $this->sendHttpHeaders();
- }
-
- /**
- * Sends the HTTP headers needed by image response.
- */
- protected function sendHttpHeaders()
- {
- Yii::$app->getResponse()->getHeaders()
- ->set('Pragma', 'public')
- ->set('Expires', '0')
- ->set('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
- ->set('Content-Transfer-Encoding', 'binary')
- ->set('Content-type', 'image/png');
- }
-}
diff --git a/framework/yii/web/Controller.php b/framework/yii/web/Controller.php
index 026c078..540140f 100644
--- a/framework/yii/web/Controller.php
+++ b/framework/yii/web/Controller.php
@@ -8,12 +8,14 @@
namespace yii\web;
use Yii;
-use yii\base\HttpException;
use yii\base\InlineAction;
+use yii\helpers\Html;
/**
- * Controller is the base class of Web controllers.
+ * Controller is the base class of web controllers.
*
+ * @property string $canonicalUrl The canonical URL of the currently requested page. This property is
+ * read-only.
*
* @author Qiang Xue
* @since 2.0
@@ -21,6 +23,16 @@ use yii\base\InlineAction;
class Controller extends \yii\base\Controller
{
/**
+ * @var boolean whether to enable CSRF validation for the actions in this controller.
+ * CSRF validation is enabled only when both this property and [[Request::enableCsrfValidation]] are true.
+ */
+ public $enableCsrfValidation = true;
+ /**
+ * @var array the parameters bound to the current action. This is mainly used by [[getCanonicalUrl()]].
+ */
+ public $actionParams = [];
+
+ /**
* Binds the parameters to the action.
* This method is invoked by [[Action]] when it begins to run with the given parameters.
* This method will check the parameter names that the action requires and return
@@ -29,7 +41,7 @@ class Controller extends \yii\base\Controller
* @param \yii\base\Action $action the action to be bound with parameters
* @param array $params the parameters to be bound to the action
* @return array the valid parameters that the action can run with.
- * @throws HttpException if there are missing parameters.
+ * @throws HttpException if there are missing or invalid parameters.
*/
public function bindActionParams($action, $params)
{
@@ -39,49 +51,232 @@ class Controller extends \yii\base\Controller
$method = new \ReflectionMethod($action, 'run');
}
- $args = array();
- $missing = array();
+ $args = [];
+ $missing = [];
+ $actionParams = [];
foreach ($method->getParameters() as $param) {
$name = $param->getName();
if (array_key_exists($name, $params)) {
- $args[] = $params[$name];
+ if ($param->isArray()) {
+ $args[] = $actionParams[$name] = is_array($params[$name]) ? $params[$name] : [$params[$name]];
+ } elseif (!is_array($params[$name])) {
+ $args[] = $actionParams[$name] = $params[$name];
+ } else {
+ throw new BadRequestHttpException(Yii::t('yii', 'Invalid data received for parameter "{param}".', [
+ 'param' => $name,
+ ]));
+ }
unset($params[$name]);
} elseif ($param->isDefaultValueAvailable()) {
- $args[] = $param->getDefaultValue();
+ $args[] = $actionParams[$name] = $param->getDefaultValue();
} else {
$missing[] = $name;
}
}
if (!empty($missing)) {
- throw new HttpException(400, Yii::t('yii', 'Missing required parameters: {params}', array(
- '{params}' => implode(', ', $missing),
- )));
+ throw new BadRequestHttpException(Yii::t('yii', 'Missing required parameters: {params}', [
+ 'params' => implode(', ', $missing),
+ ]));
}
+ $this->actionParams = $actionParams;
+
return $args;
}
/**
- * Creates a URL using the given route and parameters.
+ * @inheritdoc
+ */
+ public function beforeAction($action)
+ {
+ if (parent::beforeAction($action)) {
+ if ($this->enableCsrfValidation && Yii::$app->exception === null && !Yii::$app->getRequest()->validateCsrfToken()) {
+ throw new BadRequestHttpException(Yii::t('yii', 'Unable to verify your data submission.'));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Normalizes route making it suitable for UrlManager. Absolute routes are staying as is
+ * while relative routes are converted to absolute routes.
*
- * This method enhances [[UrlManager::createUrl()]] by supporting relative routes.
- * A relative route is a route without a slash, such as "view". If the route is an empty
- * string, [[route]] will be used; Otherwise, [[uniqueId]] will be prepended to a relative route.
+ * A relative route is a route without a leading slash, such as "view", "post/view".
*
- * After this route conversion, the method This method calls [[UrlManager::createUrl()]]
- * to create a URL.
+ * - If the route is an empty string, the current [[route]] will be used;
+ * - If the route contains no slashes at all, it is considered to be an action ID
+ * of the current controller and will be prepended with [[uniqueId]];
+ * - If the route has no leading slash, it is considered to be a route relative
+ * to the current module and will be prepended with the module's uniqueId.
*
* @param string $route the route. This can be either an absolute route or a relative route.
- * @param array $params the parameters (name-value pairs) to be included in the generated URL
- * @return string the created URL
+ * @return string normalized route suitable for UrlManager
*/
- public function createUrl($route, $params = array())
+ protected function getNormalizedRoute($route)
{
if (strpos($route, '/') === false) {
- // a relative route
+ // empty or an action ID
$route = $route === '' ? $this->getRoute() : $this->getUniqueId() . '/' . $route;
+ } elseif ($route[0] !== '/') {
+ // relative to module
+ $route = ltrim($this->module->getUniqueId() . '/' . $route, '/');
}
+ return $route;
+ }
+
+ /**
+ * Creates a relative URL using the given route and parameters.
+ *
+ * This method enhances [[UrlManager::createUrl()]] by supporting relative routes.
+ * A relative route is a route without a leading slash, such as "view", "post/view".
+ *
+ * - If the route is an empty string, the current [[route]] will be used;
+ * - If the route contains no slashes at all, it is considered to be an action ID
+ * of the current controller and will be prepended with [[uniqueId]];
+ * - If the route has no leading slash, it is considered to be a route relative
+ * to the current module and will be prepended with the module's uniqueId.
+ *
+ * After this route conversion, the method calls [[UrlManager::createUrl()]] to create a URL.
+ *
+ * @param string $route the route. This can be either an absolute route or a relative route.
+ * @param array $params the parameters (name-value pairs) to be included in the generated URL
+ * @return string the created relative URL
+ */
+ public function createUrl($route, $params = [])
+ {
+ $route = $this->getNormalizedRoute($route);
return Yii::$app->getUrlManager()->createUrl($route, $params);
}
+
+ /**
+ * Creates an absolute URL using the given route and parameters.
+ *
+ * This method enhances [[UrlManager::createAbsoluteUrl()]] by supporting relative routes.
+ * A relative route is a route without a leading slash, such as "view", "post/view".
+ *
+ * - If the route is an empty string, the current [[route]] will be used;
+ * - If the route contains no slashes at all, it is considered to be an action ID
+ * of the current controller and will be prepended with [[uniqueId]];
+ * - If the route has no leading slash, it is considered to be a route relative
+ * to the current module and will be prepended with the module's uniqueId.
+ *
+ * After this route conversion, the method calls [[UrlManager::createUrl()]] to create a URL.
+ *
+ * @param string $route the route. This can be either an absolute route or a relative route.
+ * @param array $params the parameters (name-value pairs) to be included in the generated URL
+ * @return string the created absolute URL
+ */
+ public function createAbsoluteUrl($route, $params = [])
+ {
+ $route = $this->getNormalizedRoute($route);
+ return Yii::$app->getUrlManager()->createAbsoluteUrl($route, $params);
+ }
+
+ /**
+ * Returns the canonical URL of the currently requested page.
+ * The canonical URL is constructed using [[route]] and [[actionParams]]. You may use the following code
+ * in the layout view to add a link tag about canonical URL:
+ *
+ * ~~~
+ * $this->registerLinkTag(['rel' => 'canonical', 'href' => Yii::$app->controller->canonicalUrl]);
+ * ~~~
+ *
+ * @return string the canonical URL of the currently requested page
+ */
+ public function getCanonicalUrl()
+ {
+ return Yii::$app->getUrlManager()->createAbsoluteUrl($this->getRoute(), $this->actionParams);
+ }
+
+ /**
+ * Redirects the browser to the specified URL.
+ * This method is a shortcut to [[Response::redirect()]].
+ *
+ * You can use it in an action by returning the [[Response]] directly:
+ *
+ * ```php
+ * // stop executing this action and redirect to login page
+ * return $this->redirect(['login']);
+ * ```
+ *
+ * @param string|array $url the URL to be redirected to. This can be in one of the following formats:
+ *
+ * - a string representing a URL (e.g. "http://example.com")
+ * - a string representing a URL alias (e.g. "@example.com")
+ * - an array in the format of `[$route, ...name-value pairs...]` (e.g. `['site/index', 'ref' => 1]`)
+ * [[Html::url()]] will be used to convert the array into a URL.
+ *
+ * Any relative URL will be converted into an absolute one by prepending it with the host info
+ * of the current request.
+ *
+ * @param integer $statusCode the HTTP status code. Defaults to 302.
+ * See [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html]]
+ * for details about HTTP status code
+ * @return Response the current response object
+ */
+ public function redirect($url, $statusCode = 302)
+ {
+ return Yii::$app->getResponse()->redirect(Html::url($url), $statusCode);
+ }
+
+ /**
+ * Redirects the browser to the home page.
+ *
+ * You can use this method in an action by returning the [[Response]] directly:
+ *
+ * ```php
+ * // stop executing this action and redirect to home page
+ * return $this->goHome();
+ * ```
+ *
+ * @return Response the current response object
+ */
+ public function goHome()
+ {
+ return Yii::$app->getResponse()->redirect(Yii::$app->getHomeUrl());
+ }
+
+ /**
+ * Redirects the browser to the last visited page.
+ *
+ * You can use this method in an action by returning the [[Response]] directly:
+ *
+ * ```php
+ * // stop executing this action and redirect to last visited page
+ * return $this->goBack();
+ * ```
+ *
+ * @param string|array $defaultUrl the default return URL in case it was not set previously.
+ * If this is null and the return URL was not set previously, [[Application::homeUrl]] will be redirected to.
+ * Please refer to [[User::setReturnUrl()]] on accepted format of the URL.
+ * @return Response the current response object
+ * @see User::getReturnUrl()
+ */
+ public function goBack($defaultUrl = null)
+ {
+ return Yii::$app->getResponse()->redirect(Yii::$app->getUser()->getReturnUrl($defaultUrl));
+ }
+
+ /**
+ * Refreshes the current page.
+ * This method is a shortcut to [[Response::refresh()]].
+ *
+ * You can use it in an action by returning the [[Response]] directly:
+ *
+ * ```php
+ * // stop executing this action and refresh the current page
+ * return $this->refresh();
+ * ```
+ *
+ * @param string $anchor the anchor that should be appended to the redirection URL.
+ * Defaults to empty. Make sure the anchor starts with '#' if you want to specify it.
+ * @return Response the response object itself
+ */
+ public function refresh($anchor = '')
+ {
+ return Yii::$app->getResponse()->redirect(Yii::$app->getRequest()->getUrl() . $anchor);
+ }
}
diff --git a/framework/yii/web/CookieCollection.php b/framework/yii/web/CookieCollection.php
index 3e22092..3cf80ff 100644
--- a/framework/yii/web/CookieCollection.php
+++ b/framework/yii/web/CookieCollection.php
@@ -15,7 +15,9 @@ use yii\base\Object;
/**
* CookieCollection maintains the cookies available in the current request.
*
- * @property integer $count the number of cookies in the collection
+ * @property integer $count The number of cookies in the collection. This property is read-only.
+ * @property ArrayIterator $iterator An iterator for traversing the cookies in the collection. This property
+ * is read-only.
*
* @author Qiang Xue
* @since 2.0
@@ -30,7 +32,7 @@ class CookieCollection extends Object implements \IteratorAggregate, \ArrayAcces
/**
* @var Cookie[] the cookies in this collection (indexed by the cookie names)
*/
- private $_cookies = array();
+ private $_cookies = [];
/**
* Constructor.
@@ -38,7 +40,7 @@ class CookieCollection extends Object implements \IteratorAggregate, \ArrayAcces
* an array of name-value pairs.s
* @param array $config name-value pairs that will be used to initialize the object properties
*/
- public function __construct($cookies = array(), $config = array())
+ public function __construct($cookies = [], $config = [])
{
$this->_cookies = $cookies;
parent::__construct($config);
@@ -139,10 +141,10 @@ class CookieCollection extends Object implements \IteratorAggregate, \ArrayAcces
$cookie->expire = 1;
$cookie->value = '';
} else {
- $cookie = new Cookie(array(
+ $cookie = new Cookie([
'name' => $cookie,
'expire' => 1,
- ));
+ ]);
}
if ($removeFromBrowser) {
$this->_cookies[$cookie->name] = $cookie;
@@ -160,7 +162,7 @@ class CookieCollection extends Object implements \IteratorAggregate, \ArrayAcces
if ($this->readOnly) {
throw new InvalidCallException('The cookie collection is read only.');
}
- $this->_cookies = array();
+ $this->_cookies = [];
}
/**
diff --git a/framework/yii/web/DbSession.php b/framework/yii/web/DbSession.php
index e81aa7f..d5d1742 100644
--- a/framework/yii/web/DbSession.php
+++ b/framework/yii/web/DbSession.php
@@ -17,17 +17,20 @@ use yii\base\InvalidConfigException;
*
* By default, DbSession stores session data in a DB table named 'tbl_session'. This table
* must be pre-created. The table name can be changed by setting [[sessionTable]].
- *
+ *
* The following example shows how you can configure the application to use DbSession:
- *
+ * Add the following to your application config under `components`:
+ *
* ~~~
- * 'session' => array(
+ * 'session' => [
* 'class' => 'yii\web\DbSession',
* // 'db' => 'mydb',
* // 'sessionTable' => 'my_session',
- * )
+ * ]
* ~~~
*
+ * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -109,13 +112,13 @@ class DbSession extends Session
$query = new Query;
$row = $query->from($this->sessionTable)
- ->where(array('id' => $oldID))
+ ->where(['id' => $oldID])
->createCommand($this->db)
- ->queryRow();
+ ->queryOne();
if ($row !== false) {
if ($deleteOldSession) {
$this->db->createCommand()
- ->update($this->sessionTable, array('id' => $newID), array('id' => $oldID))
+ ->update($this->sessionTable, ['id' => $newID], ['id' => $oldID])
->execute();
} else {
$row['id'] = $newID;
@@ -126,10 +129,10 @@ class DbSession extends Session
} else {
// shouldn't reach here normally
$this->db->createCommand()
- ->insert($this->sessionTable, array(
+ ->insert($this->sessionTable, [
'id' => $newID,
'expire' => time() + $this->getTimeout(),
- ))->execute();
+ ])->execute();
}
}
@@ -142,9 +145,9 @@ class DbSession extends Session
public function readSession($id)
{
$query = new Query;
- $data = $query->select(array('data'))
+ $data = $query->select(['data'])
->from($this->sessionTable)
- ->where('[[expire]]>:expire AND [[id]]=:id', array(':expire' => time(), ':id' => $id))
+ ->where('[[expire]]>:expire AND [[id]]=:id', [':expire' => time(), ':id' => $id])
->createCommand($this->db)
->queryScalar();
return $data === false ? '' : $data;
@@ -164,21 +167,21 @@ class DbSession extends Session
try {
$expire = time() + $this->getTimeout();
$query = new Query;
- $exists = $query->select(array('id'))
+ $exists = $query->select(['id'])
->from($this->sessionTable)
- ->where(array('id' => $id))
+ ->where(['id' => $id])
->createCommand($this->db)
->queryScalar();
if ($exists === false) {
$this->db->createCommand()
- ->insert($this->sessionTable, array(
+ ->insert($this->sessionTable, [
'id' => $id,
'data' => $data,
'expire' => $expire,
- ))->execute();
+ ])->execute();
} else {
$this->db->createCommand()
- ->update($this->sessionTable, array('data' => $data, 'expire' => $expire), array('id' => $id))
+ ->update($this->sessionTable, ['data' => $data, 'expire' => $expire], ['id' => $id])
->execute();
}
} catch (\Exception $e) {
@@ -200,7 +203,7 @@ class DbSession extends Session
public function destroySession($id)
{
$this->db->createCommand()
- ->delete($this->sessionTable, array('id' => $id))
+ ->delete($this->sessionTable, ['id' => $id])
->execute();
return true;
}
@@ -214,7 +217,7 @@ class DbSession extends Session
public function gcSession($maxLifetime)
{
$this->db->createCommand()
- ->delete($this->sessionTable, '[[expire]]<:expire', array(':expire' => time()))
+ ->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()])
->execute();
return true;
}
diff --git a/framework/yii/web/ErrorAction.php b/framework/yii/web/ErrorAction.php
new file mode 100644
index 0000000..95f17be
--- /dev/null
+++ b/framework/yii/web/ErrorAction.php
@@ -0,0 +1,106 @@
+ ['class' => 'yii\web\ErrorAction'],
+ * ];
+ * }
+ * ```
+ *
+ * Then, create a view file for this action. If the route of your error action is `site/error`, then
+ * the view file should be `views/site/error.php`. In this view file, the following variables are available:
+ *
+ * - `$name`: the error name
+ * - `$message`: the error message
+ * - `$exception`: the exception being handled
+ *
+ * Finally, configure the "errorHandler" application component as follows,
+ *
+ * ```php
+ * 'errorHandler' => [
+ * 'errorAction' => 'site/error',
+ * ]
+ * ```
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class ErrorAction extends Action
+{
+ /**
+ * @var string the view file to be rendered. If not set, it will take the value of [[id]].
+ * That means, if you name the action as "error" in "SiteController", then the view name
+ * would be "error", and the corresponding view file would be "views/site/error.php".
+ */
+ public $view;
+ /**
+ * @var string the name of the error when the exception name cannot be determined.
+ * Defaults to "Error".
+ */
+ public $defaultName;
+ /**
+ * @var string the message to be displayed when the exception message contains sensitive information.
+ * Defaults to "An internal server error occurred.".
+ */
+ public $defaultMessage;
+
+
+ public function run()
+ {
+ if (($exception = Yii::$app->exception) === null) {
+ return '';
+ }
+
+ if ($exception instanceof HttpException) {
+ $code = $exception->statusCode;
+ } else {
+ $code = $exception->getCode();
+ }
+ if ($exception instanceof Exception) {
+ $name = $exception->getName();
+ } else {
+ $name = $this->defaultName ?: Yii::t('yii', 'Error');
+ }
+ if ($code) {
+ $name .= " (#$code)";
+ }
+
+ if ($exception instanceof UserException) {
+ $message = $exception->getMessage();
+ } else {
+ $message = $this->defaultMessage ?: Yii::t('yii', 'An internal server error occurred.');
+ }
+
+ if (Yii::$app->getRequest()->getIsAjax()) {
+ return "$name: $message";
+ } else {
+ return $this->controller->render($this->view ?: $this->id, [
+ 'name' => $name,
+ 'message' => $message,
+ 'exception' => $exception,
+ ]);
+ }
+ }
+}
diff --git a/framework/yii/web/HeaderCollection.php b/framework/yii/web/HeaderCollection.php
index ed9ec6f..e8e4f9c 100644
--- a/framework/yii/web/HeaderCollection.php
+++ b/framework/yii/web/HeaderCollection.php
@@ -14,6 +14,10 @@ use ArrayIterator;
/**
* HeaderCollection is used by [[Response]] to maintain the currently registered HTTP headers.
*
+ * @property integer $count The number of headers in the collection. This property is read-only.
+ * @property ArrayIterator $iterator An iterator for traversing the headers in the collection. This property
+ * is read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -22,7 +26,7 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
/**
* @var array the headers in this collection (indexed by the header names)
*/
- private $_headers = array();
+ private $_headers = [];
/**
* Returns an iterator for traversing the headers in the collection.
@@ -79,7 +83,7 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
* If there is already a header with the same name, it will be replaced.
* @param string $name the name of the header
* @param string $value the value of the header
- * @return HeaderCollection the collection object itself
+ * @return static the collection object itself
*/
public function set($name, $value = '')
{
@@ -94,7 +98,7 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
* be appended to it instead of replacing it.
* @param string $name the name of the header
* @param string $value the value of the header
- * @return HeaderCollection the collection object itself
+ * @return static the collection object itself
*/
public function add($name, $value)
{
@@ -104,6 +108,22 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
}
/**
+ * Sets a new header only if it does not exist yet.
+ * If there is already a header with the same name, the new one will be ignored.
+ * @param string $name the name of the header
+ * @param string $value the value of the header
+ * @return static the collection object itself
+ */
+ public function setDefault($name, $value)
+ {
+ $name = strtolower($name);
+ if (empty($this->_headers[$name])) {
+ $this->_headers[$name][] = $value;
+ }
+ return $this;
+ }
+
+ /**
* Returns a value indicating whether the named header exists.
* @param string $name the name of the header
* @return boolean whether the named header exists
@@ -136,7 +156,7 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
*/
public function removeAll()
{
- $this->_headers = array();
+ $this->_headers = [];
}
/**
diff --git a/framework/yii/web/HttpCache.php b/framework/yii/web/HttpCache.php
index cc9e6ed..134df71 100644
--- a/framework/yii/web/HttpCache.php
+++ b/framework/yii/web/HttpCache.php
@@ -12,6 +12,33 @@ use yii\base\ActionFilter;
use yii\base\Action;
/**
+ * The HttpCache provides functionality for caching via HTTP Last-Modified and Etag headers.
+ *
+ * It is an action filter that can be added to a controller and handles the `beforeAction` event.
+ *
+ * To use AccessControl, declare it in the `behaviors()` method of your controller class.
+ * In the following example the filter will be applied to the `list`-action and
+ * the Last-Modified header will contain the date of the last update to the user table in the database.
+ *
+ * ~~~
+ * public function behaviors()
+ * {
+ * return [
+ * 'httpCache' => [
+ * 'class' => \yii\web\HttpCache::className(),
+ * 'only' => ['list'],
+ * 'lastModified' => function ($action, $params) {
+ * $q = new Query();
+ * return strtotime($q->from('users')->max('updated_timestamp'));
+ * },
+ * // 'etagSeed' => function ($action, $params) {
+ * // return // generate etag seed here
+ * // }
+ * ],
+ * ];
+ * }
+ * ~~~
+ *
* @author Da:Sourcerer
* @author Qiang Xue
* @since 2.0
diff --git a/framework/yii/web/HttpException.php b/framework/yii/web/HttpException.php
new file mode 100644
index 0000000..2398437
--- /dev/null
+++ b/framework/yii/web/HttpException.php
@@ -0,0 +1,72 @@
+
+ * @since 2.0
+ */
+class HttpException extends UserException
+{
+ /**
+ * @var integer HTTP status code, such as 403, 404, 500, etc.
+ */
+ public $statusCode;
+
+ /**
+ * Constructor.
+ * @param integer $status HTTP status code, such as 404, 500, etc.
+ * @param string $message error message
+ * @param integer $code error code
+ * @param \Exception $previous The previous exception used for the exception chaining.
+ */
+ public function __construct($status, $message = null, $code = 0, \Exception $previous = null)
+ {
+ $this->statusCode = $status;
+ parent::__construct($message, $code, $previous);
+ }
+
+ /**
+ * @return string the user-friendly name of this exception
+ */
+ public function getName()
+ {
+ if (isset(Response::$httpStatuses[$this->statusCode])) {
+ return Response::$httpStatuses[$this->statusCode];
+ } else {
+ return 'Error';
+ }
+ }
+
+ /**
+ * Returns the array representation of this object.
+ * @return array the array representation of this object.
+ */
+ public function toArray()
+ {
+ $array = parent::toArray();
+ $array['status'] = $this->statusCode;
+ return $array;
+ }
+}
diff --git a/framework/yii/web/IAssetConverter.php b/framework/yii/web/IAssetConverter.php
deleted file mode 100644
index 6021963..0000000
--- a/framework/yii/web/IAssetConverter.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- * @since 2.0
- */
-interface IAssetConverter
-{
- /**
- * Converts a given asset file into a CSS or JS file.
- * @param string $asset the asset file path, relative to $basePath
- * @param string $basePath the directory the $asset is relative to.
- * @return string the converted asset file path, relative to $basePath.
- */
- public function convert($asset, $basePath);
-}
diff --git a/framework/yii/web/Identity.php b/framework/yii/web/Identity.php
deleted file mode 100644
index 101ecdb..0000000
--- a/framework/yii/web/Identity.php
+++ /dev/null
@@ -1,81 +0,0 @@
-id;
- * }
- *
- * public function getAuthKey()
- * {
- * return $this->authKey;
- * }
- *
- * public function validateAuthKey($authKey)
- * {
- * return $this->authKey === $authKey;
- * }
- * }
- * ~~~
- *
- * @author Qiang Xue
- * @since 2.0
- */
-interface Identity
-{
- /**
- * Finds an identity by the given ID.
- * @param string|integer $id the ID to be looked for
- * @return Identity the identity object that matches the given ID.
- * Null should be returned if such an identity cannot be found
- * or the identity is not in an active state (disabled, deleted, etc.)
- */
- public static function findIdentity($id);
- /**
- * Returns an ID that can uniquely identify a user identity.
- * @return string|integer an ID that uniquely identifies a user identity.
- */
- public function getId();
- /**
- * Returns a key that can be used to check the validity of a given identity ID.
- *
- * The key should be unique for each individual user, and should be persistent
- * so that it can be used to check the validity of the user identity.
- *
- * The space of such keys should be big enough to defeat potential identity attacks.
- *
- * This is required if [[User::enableAutoLogin]] is enabled.
- * @return string a key that is used to check the validity of a given identity ID.
- * @see validateAuthKey()
- */
- public function getAuthKey();
- /**
- * Validates the given auth key.
- *
- * This is required if [[User::enableAutoLogin]] is enabled.
- * @param string $authKey the given auth key
- * @return boolean whether the given auth key is valid.
- * @see getAuthKey()
- */
- public function validateAuthKey($authKey);
-}
diff --git a/framework/yii/web/IdentityInterface.php b/framework/yii/web/IdentityInterface.php
new file mode 100644
index 0000000..c796b50
--- /dev/null
+++ b/framework/yii/web/IdentityInterface.php
@@ -0,0 +1,81 @@
+id;
+ * }
+ *
+ * public function getAuthKey()
+ * {
+ * return $this->authKey;
+ * }
+ *
+ * public function validateAuthKey($authKey)
+ * {
+ * return $this->authKey === $authKey;
+ * }
+ * }
+ * ~~~
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+interface IdentityInterface
+{
+ /**
+ * Finds an identity by the given ID.
+ * @param string|integer $id the ID to be looked for
+ * @return IdentityInterface the identity object that matches the given ID.
+ * Null should be returned if such an identity cannot be found
+ * or the identity is not in an active state (disabled, deleted, etc.)
+ */
+ public static function findIdentity($id);
+ /**
+ * Returns an ID that can uniquely identify a user identity.
+ * @return string|integer an ID that uniquely identifies a user identity.
+ */
+ public function getId();
+ /**
+ * Returns a key that can be used to check the validity of a given identity ID.
+ *
+ * The key should be unique for each individual user, and should be persistent
+ * so that it can be used to check the validity of the user identity.
+ *
+ * The space of such keys should be big enough to defeat potential identity attacks.
+ *
+ * This is required if [[User::enableAutoLogin]] is enabled.
+ * @return string a key that is used to check the validity of a given identity ID.
+ * @see validateAuthKey()
+ */
+ public function getAuthKey();
+ /**
+ * Validates the given auth key.
+ *
+ * This is required if [[User::enableAutoLogin]] is enabled.
+ * @param string $authKey the given auth key
+ * @return boolean whether the given auth key is valid.
+ * @see getAuthKey()
+ */
+ public function validateAuthKey($authKey);
+}
diff --git a/framework/yii/web/JqueryAsset.php b/framework/yii/web/JqueryAsset.php
new file mode 100644
index 0000000..90d2df6
--- /dev/null
+++ b/framework/yii/web/JqueryAsset.php
@@ -0,0 +1,22 @@
+
+ * @since 2.0
+ */
+class JqueryAsset extends AssetBundle
+{
+ public $sourcePath = '@vendor/yiisoft/jquery';
+ public $js = [
+ 'jquery.js',
+ ];
+}
diff --git a/framework/yii/web/JsExpression.php b/framework/yii/web/JsExpression.php
index 027c065..1d05b57 100644
--- a/framework/yii/web/JsExpression.php
+++ b/framework/yii/web/JsExpression.php
@@ -11,8 +11,10 @@ use yii\base\Object;
/**
* JsExpression marks a string as a JavaScript expression.
- * When using [[Json::encode()]] to encode a value, JsonExpression objects
+ *
+ * When using [[yii\helpers\Json::encode()]] to encode a value, JsonExpression objects
* will be specially handled and encoded as a JavaScript expression instead of a string.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -28,7 +30,7 @@ class JsExpression extends Object
* @param string $expression the JavaScript expression represented by this object
* @param array $config additional configurations for this object
*/
- public function __construct($expression, $config = array())
+ public function __construct($expression, $config = [])
{
$this->expression = $expression;
parent::__construct($config);
diff --git a/framework/yii/web/MethodNotAllowedHttpException.php b/framework/yii/web/MethodNotAllowedHttpException.php
new file mode 100644
index 0000000..d894f57
--- /dev/null
+++ b/framework/yii/web/MethodNotAllowedHttpException.php
@@ -0,0 +1,28 @@
+
+ * @since 2.0
+ */
+class MethodNotAllowedHttpException extends HttpException
+{
+ /**
+ * Constructor.
+ * @param string $message error message
+ * @param integer $code error code
+ * @param \Exception $previous The previous exception used for the exception chaining.
+ */
+ public function __construct($message = null, $code = 0, \Exception $previous = null)
+ {
+ parent::__construct(405, $message, $code, $previous);
+ }
+}
diff --git a/framework/yii/web/NotFoundHttpException.php b/framework/yii/web/NotFoundHttpException.php
new file mode 100644
index 0000000..71f246d
--- /dev/null
+++ b/framework/yii/web/NotFoundHttpException.php
@@ -0,0 +1,28 @@
+
+ * @since 2.0
+ */
+class NotFoundHttpException extends HttpException
+{
+ /**
+ * Constructor.
+ * @param string $message error message
+ * @param integer $code error code
+ * @param \Exception $previous The previous exception used for the exception chaining.
+ */
+ public function __construct($message = null, $code = 0, \Exception $previous = null)
+ {
+ parent::__construct(404, $message, $code, $previous);
+ }
+}
diff --git a/framework/yii/web/PageCache.php b/framework/yii/web/PageCache.php
index 8b28e62..4c8cc50 100644
--- a/framework/yii/web/PageCache.php
+++ b/framework/yii/web/PageCache.php
@@ -10,10 +10,40 @@ namespace yii\web;
use Yii;
use yii\base\ActionFilter;
use yii\base\Action;
-use yii\base\View;
use yii\caching\Dependency;
/**
+ * The PageCache provides functionality for whole page caching
+ *
+ * It is an action filter that can be added to a controller and handles the `beforeAction` event.
+ *
+ * To use PageCache, declare it in the `behaviors()` method of your controller class.
+ * In the following example the filter will be applied to the `list`-action and
+ * cache the whole page for maximum 60 seconds or until the count of entries in the post table changes.
+ * It also stores different versions of the page depended on the route ([[varyByRoute]] is true by default),
+ * the application language and user id.
+ *
+ * ~~~
+ * public function behaviors()
+ * {
+ * return [
+ * 'pageCache' => [
+ * 'class' => \yii\web\PageCache::className(),
+ * 'only' => ['list'],
+ * 'duration' => 60,
+ * 'dependecy' => [
+ * 'class' => 'yii\caching\DbDependency',
+ * 'sql' => 'SELECT COUNT(*) FROM post',
+ * ],
+ * 'variations' => [
+ * Yii::$app->language,
+ * Yii::$app->user->id
+ * ]
+ * ],
+ * ];
+ * }
+ * ~~~
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -39,10 +69,10 @@ class PageCache extends ActionFilter
* For example,
*
* ~~~
- * array(
+ * [
* 'class' => 'yii\caching\DbDependency',
* 'sql' => 'SELECT MAX(lastModified) FROM Post',
- * )
+ * ]
* ~~~
*
* would make the output cache depends on the last modified time of all posts.
@@ -56,9 +86,10 @@ class PageCache extends ActionFilter
* according to the current application language:
*
* ~~~
- * array(
+ * [
* Yii::$app->language,
- * )
+ * ]
+ * ~~~
*/
public $variations;
/**
@@ -66,6 +97,11 @@ class PageCache extends ActionFilter
* the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests).
*/
public $enabled = true;
+ /**
+ * @var \yii\base\View the view component to use for caching. If not set, the default application view component
+ * [[Application::view]] will be used.
+ */
+ public $view;
public function init()
@@ -84,21 +120,31 @@ class PageCache extends ActionFilter
*/
public function beforeAction($action)
{
- $properties = array();
- foreach (array('cache', 'duration', 'dependency', 'variations', 'enabled') as $name) {
+ $properties = [];
+ foreach (['cache', 'duration', 'dependency', 'variations', 'enabled'] as $name) {
$properties[$name] = $this->$name;
}
$id = $this->varyByRoute ? $action->getUniqueId() : __CLASS__;
- return $this->view->beginCache($id, $properties);
+ ob_start();
+ ob_implicit_flush(false);
+ if ($this->view->beginCache($id, $properties)) {
+ return true;
+ } else {
+ Yii::$app->getResponse()->content = ob_get_clean();
+ return false;
+ }
}
/**
* This method is invoked right after an action is executed.
* You may override this method to do some postprocessing for the action.
* @param Action $action the action just executed.
+ * @param mixed $result the action execution result
*/
- public function afterAction($action)
+ public function afterAction($action, &$result)
{
+ echo $result;
$this->view->endCache();
+ $result = ob_get_clean();
}
}
diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php
index 6f5cdb5..9736043 100644
--- a/framework/yii/web/Request.php
+++ b/framework/yii/web/Request.php
@@ -8,46 +8,118 @@
namespace yii\web;
use Yii;
-use yii\base\HttpException;
use yii\base\InvalidConfigException;
-use yii\helpers\SecurityHelper;
+use yii\helpers\Security;
/**
+ * The web Request class represents an HTTP request
+ *
+ * It encapsulates the $_SERVER variable and resolves its inconsistency among different Web servers.
+ * Also it provides an interface to retrieve request parameters from $_POST, $_GET, $_COOKIES and REST
+ * parameters sent via other HTTP methods like PUT or DELETE.
+ *
+ * Request is configured as an application component in [[yii\web\Application]] by default.
+ * You can access that instance via `Yii::$app->request`.
+ *
+ * @property string $absoluteUrl The currently requested absolute URL. This property is read-only.
+ * @property string $acceptTypes User browser accept types, null if not present. This property is read-only.
+ * @property array $acceptedContentTypes The content types ordered by the preference level. The first element
+ * represents the most preferred content type.
+ * @property array $acceptedLanguages The languages ordered by the preference level. The first element
+ * represents the most preferred language.
+ * @property string $baseUrl The relative URL for the application.
+ * @property string $cookieValidationKey The secret key used for cookie validation. If it was not set
+ * previously, a random key will be generated and used.
+ * @property CookieCollection $cookies The cookie collection. This property is read-only.
+ * @property string $csrfToken The random token for CSRF validation. This property is read-only.
+ * @property string $csrfTokenFromHeader The CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned
+ * if no such header is sent. This property is read-only.
+ * @property array $delete The DELETE request parameter values. This property is read-only.
+ * @property string $hostInfo Schema and hostname part (with port number if needed) of the request URL (e.g.
+ * `http://www.yiiframework.com`).
+ * @property boolean $isAjax Whether this is an AJAX (XMLHttpRequest) request. This property is read-only.
+ * @property boolean $isDelete Whether this is a DELETE request. This property is read-only.
+ * @property boolean $isFlash Whether this is an Adobe Flash or Adobe Flex request. This property is
+ * read-only.
+ * @property boolean $isGet Whether this is a GET request. This property is read-only.
+ * @property boolean $isHead Whether this is a HEAD request. This property is read-only.
+ * @property boolean $isOptions Whether this is a OPTIONS request. This property is read-only.
+ * @property boolean $isPatch Whether this is a PATCH request. This property is read-only.
+ * @property boolean $isPost Whether this is a POST request. This property is read-only.
+ * @property boolean $isPut Whether this is a PUT request. This property is read-only.
+ * @property boolean $isSecureConnection If the request is sent via secure channel (https). This property is
+ * read-only.
+ * @property string $method Request method, such as GET, POST, HEAD, PUT, PATCH, DELETE. The value returned is
+ * turned into upper case. This property is read-only.
+ * @property array $patch The PATCH request parameter values. This property is read-only.
+ * @property string $pathInfo Part of the request URL that is after the entry script and before the question
+ * mark. Note, the returned path info is already URL-decoded.
+ * @property integer $port Port number for insecure requests.
+ * @property array $post The POST request parameter values. This property is read-only.
+ * @property string $preferredLanguage The language that the application should use. Null is returned if both
+ * [[getAcceptedLanguages()]] and `$languages` are empty. This property is read-only.
+ * @property array $put The PUT request parameter values. This property is read-only.
+ * @property string $queryString Part of the request URL that is after the question mark. This property is
+ * read-only.
+ * @property string $rawBody The request body. This property is read-only.
+ * @property string $referrer URL referrer, null if not present. This property is read-only.
+ * @property array $restParams The RESTful request parameters.
+ * @property string $scriptFile The entry script file path.
+ * @property string $scriptUrl The relative URL of the entry script.
+ * @property integer $securePort Port number for secure requests.
+ * @property string $serverName Server name. This property is read-only.
+ * @property integer $serverPort Server port number. This property is read-only.
+ * @property string $url The currently requested relative URL. Note that the URI returned is URL-encoded.
+ * @property string $userAgent User agent, null if not present. This property is read-only.
+ * @property string $userHost User host name, null if cannot be determined. This property is read-only.
+ * @property string $userIP User IP address. This property is read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
class Request extends \yii\base\Request
{
/**
- * @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to false.
- * By setting this property to true, forms submitted to an Yii Web application must be originated
+ * The name of the HTTP header for sending CSRF token.
+ */
+ const CSRF_HEADER = 'X-CSRF-Token';
+
+
+ /**
+ * @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to true.
+ * When CSRF validation is enabled, forms submitted to an Yii Web application must be originated
* from the same application. If not, a 400 HTTP exception will be raised.
*
* Note, this feature requires that the user client accepts cookie. Also, to use this feature,
- * forms submitted via POST method must contain a hidden input whose name is specified by [[csrfTokenName]].
+ * forms submitted via POST method must contain a hidden input whose name is specified by [[csrfVar]].
* You may use [[\yii\web\Html::beginForm()]] to generate his hidden input.
+ *
+ * In JavaScript, you may get the values of [[csrfVar]] and [[csrfToken]] via `yii.getCsrfVar()` and
+ * `yii.getCsrfToken()`, respectively. The [[\yii\web\YiiAsset]] asset must be registered.
+ *
+ * @see Controller::enableCsrfValidation
* @see http://en.wikipedia.org/wiki/Cross-site_request_forgery
*/
- public $enableCsrfValidation = false;
+ public $enableCsrfValidation = true;
/**
- * @var string the name of the token used to prevent CSRF. Defaults to 'YII_CSRF_TOKEN'.
- * This property is effectively only when {@link enableCsrfValidation} is true.
+ * @var string the name of the token used to prevent CSRF. Defaults to '_csrf'.
+ * This property is used only when [[enableCsrfValidation]] is true.
*/
- public $csrfTokenName = '_csrf';
+ public $csrfVar = '_csrf';
/**
* @var array the configuration of the CSRF cookie. This property is used only when [[enableCsrfValidation]] is true.
* @see Cookie
*/
- public $csrfCookie = array('httpOnly' => true);
+ public $csrfCookie = ['httpOnly' => true];
/**
* @var boolean whether cookies should be validated to ensure they are not tampered. Defaults to true.
*/
public $enableCookieValidation = true;
/**
- * @var string|boolean the name of the POST parameter that is used to indicate if a request is a PUT or DELETE
+ * @var string|boolean the name of the POST parameter that is used to indicate if a request is a PUT, PATCH or DELETE
* request tunneled through POST. Default to '_method'.
- * @see getMethod
- * @see getRestParams
+ * @see getMethod()
+ * @see getRestParams()
*/
public $restVar = '_method';
@@ -61,21 +133,19 @@ class Request extends \yii\base\Request
*/
public function resolve()
{
- $this->validateCsrfToken();
-
$result = Yii::$app->getUrlManager()->parseRequest($this);
if ($result !== false) {
list ($route, $params) = $result;
$_GET = array_merge($_GET, $params);
- return array($route, $_GET);
+ return [$route, $_GET];
} else {
- throw new HttpException(404, Yii::t('yii', 'Page not found.'));
+ throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));
}
}
/**
- * Returns the method of the current request (e.g. GET, POST, HEAD, PUT, DELETE).
- * @return string request method, such as GET, POST, HEAD, PUT, DELETE.
+ * Returns the method of the current request (e.g. GET, POST, HEAD, PUT, PATCH, DELETE).
+ * @return string request method, such as GET, POST, HEAD, PUT, PATCH, DELETE.
* The value returned is turned into upper case.
*/
public function getMethod()
@@ -88,6 +158,33 @@ class Request extends \yii\base\Request
}
/**
+ * Returns whether this is a GET request.
+ * @return boolean whether this is a GET request.
+ */
+ public function getIsGet()
+ {
+ return $this->getMethod() === 'GET';
+ }
+
+ /**
+ * Returns whether this is an OPTIONS request.
+ * @return boolean whether this is a OPTIONS request.
+ */
+ public function getIsOptions()
+ {
+ return $this->getMethod() === 'OPTIONS';
+ }
+
+ /**
+ * Returns whether this is a HEAD request.
+ * @return boolean whether this is a HEAD request.
+ */
+ public function getIsHead()
+ {
+ return $this->getMethod() === 'HEAD';
+ }
+
+ /**
* Returns whether this is a POST request.
* @return boolean whether this is a POST request.
*/
@@ -115,6 +212,15 @@ class Request extends \yii\base\Request
}
/**
+ * Returns whether this is a PATCH request.
+ * @return boolean whether this is a PATCH request.
+ */
+ public function getIsPatch()
+ {
+ return $this->getMethod() === 'PATCH';
+ }
+
+ /**
* Returns whether this is an AJAX (XMLHttpRequest) request.
* @return boolean whether this is an AJAX (XMLHttpRequest) request.
*/
@@ -138,7 +244,7 @@ class Request extends \yii\base\Request
/**
* Returns the request parameters for the RESTful request.
* @return array the RESTful request parameters
- * @see getMethod
+ * @see getMethod()
*/
public function getRestParams()
{
@@ -146,12 +252,8 @@ class Request extends \yii\base\Request
if (isset($_POST[$this->restVar])) {
$this->_restParams = $_POST;
} else {
- $this->_restParams = array();
- if (function_exists('mb_parse_str')) {
- mb_parse_str($this->getRawBody(), $this->_restParams);
- } else {
- parse_str($this->getRawBody(), $this->_restParams);
- }
+ $this->_restParams = [];
+ mb_parse_str($this->getRawBody(), $this->_restParams);
}
}
return $this->_restParams;
@@ -195,51 +297,81 @@ class Request extends \yii\base\Request
/**
* Returns the named GET parameter value.
* If the GET parameter does not exist, the second parameter to this method will be returned.
- * @param string $name the GET parameter name
+ * @param string $name the GET parameter name. If not specified, whole $_GET is returned.
* @param mixed $defaultValue the default parameter value if the GET parameter does not exist.
* @return mixed the GET parameter value
- * @see getPost
+ * @see getPost()
*/
- public function get($name, $defaultValue = null)
+ public function get($name = null, $defaultValue = null)
{
+ if ($name === null) {
+ return $_GET;
+ }
return isset($_GET[$name]) ? $_GET[$name] : $defaultValue;
}
/**
* Returns the named POST parameter value.
* If the POST parameter does not exist, the second parameter to this method will be returned.
- * @param string $name the POST parameter name
+ * @param string $name the POST parameter name. If not specified, whole $_POST is returned.
* @param mixed $defaultValue the default parameter value if the POST parameter does not exist.
+ * @property array the POST request parameter values
* @return mixed the POST parameter value
- * @see getParam
+ * @see get()
*/
- public function getPost($name, $defaultValue = null)
+ public function getPost($name = null, $defaultValue = null)
{
+ if ($name === null) {
+ return $_POST;
+ }
return isset($_POST[$name]) ? $_POST[$name] : $defaultValue;
}
/**
* Returns the named DELETE parameter value.
- * @param string $name the DELETE parameter name
+ * @param string $name the DELETE parameter name. If not specified, an array of DELETE parameters is returned.
* @param mixed $defaultValue the default parameter value if the DELETE parameter does not exist.
+ * @property array the DELETE request parameter values
* @return mixed the DELETE parameter value
*/
- public function getDelete($name, $defaultValue = null)
+ public function getDelete($name = null, $defaultValue = null)
{
+ if ($name === null) {
+ return $this->getRestParams();
+ }
return $this->getIsDelete() ? $this->getRestParam($name, $defaultValue) : null;
}
/**
* Returns the named PUT parameter value.
- * @param string $name the PUT parameter name
+ * @param string $name the PUT parameter name. If not specified, an array of PUT parameters is returned.
* @param mixed $defaultValue the default parameter value if the PUT parameter does not exist.
+ * @property array the PUT request parameter values
* @return mixed the PUT parameter value
*/
- public function getPut($name, $defaultValue = null)
+ public function getPut($name = null, $defaultValue = null)
{
+ if ($name === null) {
+ return $this->getRestParams();
+ }
return $this->getIsPut() ? $this->getRestParam($name, $defaultValue) : null;
}
+ /**
+ * Returns the named PATCH parameter value.
+ * @param string $name the PATCH parameter name. If not specified, an array of PATCH parameters is returned.
+ * @param mixed $defaultValue the default parameter value if the PATCH parameter does not exist.
+ * @property array the PATCH request parameter values
+ * @return mixed the PATCH parameter value
+ */
+ public function getPatch($name = null, $defaultValue = null)
+ {
+ if ($name === null) {
+ return $this->getRestParams();
+ }
+ return $this->getIsPatch() ? $this->getRestParam($name, $defaultValue) : null;
+ }
+
private $_hostInfo;
/**
@@ -248,7 +380,7 @@ class Request extends \yii\base\Request
* By default this is determined based on the user request information.
* You may explicitly specify it by setting the [[setHostInfo()|hostInfo]] property.
* @return string schema and hostname part (with port number if needed) of the request URL (e.g. `http://www.yiiframework.com`)
- * @see setHostInfo
+ * @see setHostInfo()
*/
public function getHostInfo()
{
@@ -287,7 +419,7 @@ class Request extends \yii\base\Request
* This is similar to [[scriptUrl]] except that it does not include the script file name,
* and the ending slashes are removed.
* @return string the relative URL for the application
- * @see setScriptUrl
+ * @see setScriptUrl()
*/
public function getBaseUrl()
{
@@ -398,13 +530,13 @@ class Request extends \yii\base\Request
*/
public function setPathInfo($value)
{
- $this->_pathInfo = trim($value, '/');
+ $this->_pathInfo = ltrim($value, '/');
}
/**
* Resolves the path info part of the currently requested URL.
* A path info refers to the part that is after the entry script and before the question mark (query string).
- * The starting and ending slashes are both removed.
+ * The starting slashes are both removed (ending slashes will be kept).
* @return string part of the request URL that is after the entry script and before the question mark.
* Note, the returned path info is decoded.
* @throws InvalidConfigException if the path info cannot be determined due to unexpected server configuration
@@ -446,7 +578,7 @@ class Request extends \yii\base\Request
throw new InvalidConfigException('Unable to determine the path info of the current request.');
}
- return trim($pathInfo, '/');
+ return ltrim($pathInfo, '/');
}
/**
@@ -604,7 +736,7 @@ class Request extends \yii\base\Request
* Defaults to 80, or the port specified by the server if the current
* request is insecure.
* @return integer port number for insecure requests.
- * @see setPort
+ * @see setPort()
*/
public function getPort()
{
@@ -635,7 +767,7 @@ class Request extends \yii\base\Request
* Defaults to 443, or the port specified by the server if the current
* request is secure.
* @return integer port number for secure requests.
- * @see setSecurePort
+ * @see setSecurePort()
*/
public function getSecurePort()
{
@@ -659,40 +791,133 @@ class Request extends \yii\base\Request
}
}
- private $_preferredLanguages;
+ private $_contentTypes;
/**
- * Returns the user preferred languages.
- * The languages returned are ordered by user's preference, starting with the language that the user
- * prefers the most.
- * @return string the user preferred languages. An empty array may be returned if the user has no preference.
+ * Returns the content types accepted by the end user.
+ * This is determined by the `Accept` HTTP header.
+ * @return array the content types ordered by the preference level. The first element
+ * represents the most preferred content type.
*/
- public function getPreferredLanguages()
+ public function getAcceptedContentTypes()
{
- if ($this->_preferredLanguages === null) {
- if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && ($n = preg_match_all('/([\w\-_]+)\s*(;\s*q\s*=\s*(\d*\.\d*))?/', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches)) > 0) {
- $languages = array();
- for ($i = 0; $i < $n; ++$i) {
- $languages[$matches[1][$i]] = empty($matches[3][$i]) ? 1.0 : floatval($matches[3][$i]);
- }
- arsort($languages);
- $this->_preferredLanguages = array_keys($languages);
+ if ($this->_contentTypes === null) {
+ if (isset($_SERVER['HTTP_ACCEPT'])) {
+ $this->_contentTypes = $this->parseAcceptHeader($_SERVER['HTTP_ACCEPT']);
+ } else {
+ $this->_contentTypes = [];
+ }
+ }
+ return $this->_contentTypes;
+ }
+
+ /**
+ * @param array $value the content types that are accepted by the end user. They should
+ * be ordered by the preference level.
+ */
+ public function setAcceptedContentTypes($value)
+ {
+ $this->_contentTypes = $value;
+ }
+
+ private $_languages;
+
+ /**
+ * Returns the languages accepted by the end user.
+ * This is determined by the `Accept-Language` HTTP header.
+ * @return array the languages ordered by the preference level. The first element
+ * represents the most preferred language.
+ */
+ public function getAcceptedLanguages()
+ {
+ if ($this->_languages === null) {
+ if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+ $this->_languages = $this->parseAcceptHeader($_SERVER['HTTP_ACCEPT_LANGUAGE']);
+ } else {
+ $this->_languages = [];
+ }
+ }
+ return $this->_languages;
+ }
+
+ /**
+ * @param array $value the languages that are accepted by the end user. They should
+ * be ordered by the preference level.
+ */
+ public function setAcceptedLanguages($value)
+ {
+ $this->_languages = $value;
+ }
+
+ /**
+ * Parses the given `Accept` (or `Accept-Language`) header.
+ * This method will return the accepted values ordered by their preference level.
+ * @param string $header the header to be parsed
+ * @return array the accept values ordered by their preference level.
+ */
+ protected function parseAcceptHeader($header)
+ {
+ $accepts = [];
+ $n = preg_match_all('/\s*([\w\/\-\*]+)\s*(?:;\s*q\s*=\s*([\d\.]+))?[^,]*/', $header, $matches, PREG_SET_ORDER);
+ for ($i = 0; $i < $n; ++$i) {
+ if (!empty($matches[$i][1])) {
+ $accepts[] = [$matches[$i][1], isset($matches[$i][2]) ? (float)$matches[$i][2] : 1, $i];
+ }
+ }
+ usort($accepts, function ($a, $b) {
+ if ($a[1] > $b[1]) {
+ return -1;
+ } elseif ($a[1] < $b[1]) {
+ return 1;
+ } elseif ($a[0] === $b[0]) {
+ return $a[2] > $b[2] ? 1 : -1;
+ } elseif ($a[0] === '*/*') {
+ return 1;
+ } elseif ($b[0] === '*/*') {
+ return -1;
} else {
- $this->_preferredLanguages = array();
+ $wa = $a[0][strlen($a[0]) - 1] === '*';
+ $wb = $b[0][strlen($b[0]) - 1] === '*';
+ if ($wa xor $wb) {
+ return $wa ? 1 : -1;
+ } else {
+ return $a[2] > $b[2] ? 1 : -1;
+ }
}
+ });
+ $result = [];
+ foreach ($accepts as $accept) {
+ $result[] = $accept[0];
}
- return $this->_preferredLanguages;
+ return array_unique($result);
}
/**
- * Returns the language most preferred by the user.
- * @return string|boolean the language most preferred by the user. If the user has no preference, false
- * will be returned.
+ * Returns the user-preferred language that should be used by this application.
+ * The language resolution is based on the user preferred languages and the languages
+ * supported by the application. The method will try to find the best match.
+ * @param array $languages a list of the languages supported by the application.
+ * If empty, this method will return the first language returned by [[getAcceptedLanguages()]].
+ * @return string the language that the application should use. Null is returned if both [[getAcceptedLanguages()]]
+ * and `$languages` are empty.
*/
- public function getPreferredLanguage()
+ public function getPreferredLanguage($languages = [])
{
- $languages = $this->getPreferredLanguages();
- return isset($languages[0]) ? $languages[0] : false;
+ $acceptedLanguages = $this->getAcceptedLanguages();
+ if (empty($languages)) {
+ return isset($acceptedLanguages[0]) ? $acceptedLanguages[0] : null;
+ }
+ foreach ($acceptedLanguages as $acceptedLanguage) {
+ $acceptedLanguage = str_replace('_', '-', strtolower($acceptedLanguage));
+ foreach ($languages as $language) {
+ $language = str_replace('_', '-', strtolower($language));
+ // en-us==en-us, en==en-us, en-us==en
+ if ($language === $acceptedLanguage || strpos($acceptedLanguage, $language . '-') === 0 || strpos($language, $acceptedLanguage . '-') === 0) {
+ return $language;
+ }
+ }
+ }
+ return reset($languages);
}
/**
@@ -714,9 +939,9 @@ class Request extends \yii\base\Request
public function getCookies()
{
if ($this->_cookies === null) {
- $this->_cookies = new CookieCollection($this->loadCookies(), array(
+ $this->_cookies = new CookieCollection($this->loadCookies(), [
'readOnly' => true,
- ));
+ ]);
}
return $this->_cookies;
}
@@ -727,23 +952,23 @@ class Request extends \yii\base\Request
*/
protected function loadCookies()
{
- $cookies = array();
+ $cookies = [];
if ($this->enableCookieValidation) {
$key = $this->getCookieValidationKey();
foreach ($_COOKIE as $name => $value) {
- if (is_string($value) && ($value = SecurityHelper::validateData($value, $key)) !== false) {
- $cookies[$name] = new Cookie(array(
+ if (is_string($value) && ($value = Security::validateData($value, $key)) !== false) {
+ $cookies[$name] = new Cookie([
'name' => $name,
'value' => @unserialize($value),
- ));
+ ]);
}
}
} else {
foreach ($_COOKIE as $name => $value) {
- $cookies[$name] = new Cookie(array(
+ $cookies[$name] = new Cookie([
'name' => $name,
'value' => $value,
- ));
+ ]);
}
}
return $cookies;
@@ -752,13 +977,13 @@ class Request extends \yii\base\Request
private $_cookieValidationKey;
/**
- * @return string the secret key used for cookie validation. If it was set previously,
+ * @return string the secret key used for cookie validation. If it was not set previously,
* a random key will be generated and used.
*/
public function getCookieValidationKey()
{
if ($this->_cookieValidationKey === null) {
- $this->_cookieValidationKey = SecurityHelper::getSecretKey(__CLASS__ . '/' . Yii::$app->id);
+ $this->_cookieValidationKey = Security::getSecretKey(__CLASS__ . '/' . Yii::$app->id);
}
return $this->_cookieValidationKey;
}
@@ -772,7 +997,10 @@ class Request extends \yii\base\Request
$this->_cookieValidationKey = $value;
}
- private $_csrfToken;
+ /**
+ * @var Cookie
+ */
+ private $_csrfCookie;
/**
* Returns the random token used to perform CSRF validation.
@@ -782,16 +1010,24 @@ class Request extends \yii\base\Request
*/
public function getCsrfToken()
{
- if ($this->_csrfToken === null) {
- $cookies = $this->getCookies();
- if (($this->_csrfToken = $cookies->getValue($this->csrfTokenName)) === null) {
- $cookie = $this->createCsrfCookie();
- $this->_csrfToken = $cookie->value;
- $cookies->add($cookie);
+ if ($this->_csrfCookie === null) {
+ $this->_csrfCookie = $this->getCookies()->get($this->csrfVar);
+ if ($this->_csrfCookie === null) {
+ $this->_csrfCookie = $this->createCsrfCookie();
+ Yii::$app->getResponse()->getCookies()->add($this->_csrfCookie);
}
}
- return $this->_csrfToken;
+ return $this->_csrfCookie->value;
+ }
+
+ /**
+ * @return string the CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned if no such header is sent.
+ */
+ public function getCsrfTokenFromHeader()
+ {
+ $key = 'HTTP_' . str_replace('-', '_', strtoupper(self::CSRF_HEADER));
+ return isset($_SERVER[$key]) ? $_SERVER[$key] : null;
}
/**
@@ -803,7 +1039,7 @@ class Request extends \yii\base\Request
protected function createCsrfCookie()
{
$options = $this->csrfCookie;
- $options['name'] = $this->csrfTokenName;
+ $options['name'] = $this->csrfVar;
$options['value'] = sha1(uniqid(mt_rand(), true));
return new Cookie($options);
}
@@ -812,30 +1048,30 @@ class Request extends \yii\base\Request
* Performs the CSRF validation.
* The method will compare the CSRF token obtained from a cookie and from a POST field.
* If they are different, a CSRF attack is detected and a 400 HTTP exception will be raised.
- * @throws HttpException if the validation fails
+ * This method is called in [[Controller::beforeAction()]].
+ * @return boolean whether CSRF token is valid. If [[enableCsrfValidation]] is false, this method will return true.
*/
public function validateCsrfToken()
{
- if (!$this->enableCsrfValidation) {
- return;
- }
$method = $this->getMethod();
- if ($method === 'POST' || $method === 'PUT' || $method === 'DELETE') {
- $cookies = $this->getCookies();
- switch ($method) {
- case 'POST':
- $token = $this->getPost($this->csrfTokenName);
- break;
- case 'PUT':
- $token = $this->getPut($this->csrfTokenName);
- break;
- case 'DELETE':
- $token = $this->getDelete($this->csrfTokenName);
- }
-
- if (empty($token) || $cookies->getValue($this->csrfTokenName) !== $token) {
- throw new HttpException(400, Yii::t('yii', 'Unable to verify your data submission.'));
- }
+ if (!$this->enableCsrfValidation || !in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) {
+ return true;
+ }
+ $trueToken = $this->getCookies()->getValue($this->csrfVar);
+ switch ($method) {
+ case 'PUT':
+ $token = $this->getPut($this->csrfVar);
+ break;
+ case 'PATCH':
+ $token = $this->getPatch($this->csrfVar);
+ break;
+ case 'DELETE':
+ $token = $this->getDelete($this->csrfVar);
+ break;
+ default:
+ $token = $this->getPost($this->csrfVar);
+ break;
}
+ return $token === $trueToken || $this->getCsrfTokenFromHeader() === $trueToken;
}
}
diff --git a/framework/yii/web/Response.php b/framework/yii/web/Response.php
index f337fbb..43e8f73 100644
--- a/framework/yii/web/Response.php
+++ b/framework/yii/web/Response.php
@@ -8,15 +8,52 @@
namespace yii\web;
use Yii;
-use yii\base\HttpException;
+use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
use yii\helpers\FileHelper;
use yii\helpers\Html;
use yii\helpers\Json;
-use yii\helpers\SecurityHelper;
+use yii\helpers\Security;
use yii\helpers\StringHelper;
/**
+ * The web Response class represents an HTTP response
+ *
+ * It holds the [[headers]], [[cookies]] and [[content]] that is to be sent to the client.
+ * It also controls the HTTP [[statusCode|status code]].
+ *
+ * Response is configured as an application component in [[yii\web\Application]] by default.
+ * You can access that instance via `Yii::$app->response`.
+ *
+ * You can modify its configuration by adding an array to your application config under `components`
+ * as it is shown in the following example:
+ *
+ * ~~~
+ * 'response' => [
+ * 'format' => yii\web\Response::FORMAT_JSON,
+ * 'charset' => 'UTF-8',
+ * // ...
+ * ]
+ * ~~~
+ *
+ * @property CookieCollection $cookies The cookie collection. This property is read-only.
+ * @property HeaderCollection $headers The header collection. This property is read-only.
+ * @property boolean $isClientError Whether this response indicates a client error. This property is
+ * read-only.
+ * @property boolean $isEmpty Whether this response is empty. This property is read-only.
+ * @property boolean $isForbidden Whether this response indicates the current request is forbidden. This
+ * property is read-only.
+ * @property boolean $isInformational Whether this response is informational. This property is read-only.
+ * @property boolean $isInvalid Whether this response has a valid [[statusCode]]. This property is read-only.
+ * @property boolean $isNotFound Whether this response indicates the currently requested resource is not
+ * found. This property is read-only.
+ * @property boolean $isOk Whether this response is OK. This property is read-only.
+ * @property boolean $isRedirection Whether this response is a redirection. This property is read-only.
+ * @property boolean $isServerError Whether this response indicates a server error. This property is
+ * read-only.
+ * @property boolean $isSuccessful Whether this response is successful. This property is read-only.
+ * @property integer $statusCode The HTTP status code to send with the response.
+ *
* @author Qiang Xue
* @author Carsten Brandt
* @since 2.0
@@ -24,33 +61,84 @@ use yii\helpers\StringHelper;
class Response extends \yii\base\Response
{
/**
- * @var integer the HTTP status code that should be used when redirecting in AJAX mode.
- * This is used by [[redirect()]]. A 2xx code should normally be used for this purpose
- * so that the AJAX handler will treat the response as a success.
- * @see redirect
+ * @event ResponseEvent an event that is triggered at the beginning of [[send()]].
*/
- public $ajaxRedirectCode = 278;
+ const EVENT_BEFORE_SEND = 'beforeSend';
/**
- * @var string
+ * @event ResponseEvent an event that is triggered at the end of [[send()]].
*/
- public $content;
+ const EVENT_AFTER_SEND = 'afterSend';
+ /**
+ * @event ResponseEvent an event that is triggered right after [[prepare()]] is called in [[send()]].
+ * You may respond to this event to filter the response content before it is sent to the client.
+ */
+ const EVENT_AFTER_PREPARE = 'afterPrepare';
+
+ const FORMAT_RAW = 'raw';
+ const FORMAT_HTML = 'html';
+ const FORMAT_JSON = 'json';
+ const FORMAT_JSONP = 'jsonp';
+ const FORMAT_XML = 'xml';
+
+ /**
+ * @var string the response format. This determines how to convert [[data]] into [[content]]
+ * when the latter is not set. By default, the following formats are supported:
+ *
+ * - [[FORMAT_RAW]]: the data will be treated as the response content without any conversion.
+ * No extra HTTP header will be added.
+ * - [[FORMAT_HTML]]: the data will be treated as the response content without any conversion.
+ * The "Content-Type" header will set as "text/html" if it is not set previously.
+ * - [[FORMAT_JSON]]: the data will be converted into JSON format, and the "Content-Type"
+ * header will be set as "application/json".
+ * - [[FORMAT_JSONP]]: the data will be converted into JSONP format, and the "Content-Type"
+ * header will be set as "text/javascript". Note that in this case `$data` must be an array
+ * with "data" and "callback" elements. The former refers to the actual data to be sent,
+ * while the latter refers to the name of the JavaScript callback.
+ * - [[FORMAT_XML]]: the data will be converted into XML format. Please refer to [[XmlResponseFormatter]]
+ * for more details.
+ *
+ * You may customize the formatting process or support additional formats by configuring [[formatters]].
+ * @see formatters
+ */
+ public $format = self::FORMAT_HTML;
+ /**
+ * @var array the formatters for converting data into the response content of the specified [[format]].
+ * The array keys are the format names, and the array values are the corresponding configurations
+ * for creating the formatter objects.
+ * @see format
+ */
+ public $formatters;
/**
- * @var string
+ * @var mixed the original response data. When this is not null, it will be converted into [[content]]
+ * according to [[format]] when the response is being sent out.
+ * @see content
*/
- public $statusText;
+ public $data;
/**
- * @var string the charset to use. If not set, [[\yii\base\Application::charset]] will be used.
+ * @var string the response content. When [[data]] is not null, it will be converted into [[content]]
+ * according to [[format]] when the response is being sent out.
+ * @see data
+ */
+ public $content;
+ /**
+ * @var string the charset of the text response. If not set, it will use
+ * the value of [[Application::charset]].
*/
public $charset;
/**
- * @var string the version of the HTTP protocol to use
+ * @var string the HTTP status description that comes together with the status code.
+ * @see [[httpStatuses]]
*/
- public $version = '1.0';
-
+ public $statusText = 'OK';
+ /**
+ * @var string the version of the HTTP protocol to use. If not set, it will be determined via `$_SERVER['SERVER_PROTOCOL']`,
+ * or '1.1' if that is not available.
+ */
+ public $version;
/**
* @var array list of HTTP status codes and the corresponding texts
*/
- public static $statusTexts = array(
+ public static $httpStatuses = [
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
@@ -94,7 +182,7 @@ class Response extends \yii\base\Response
415 => 'Unsupported Media Type',
416 => 'Requested range unsatisfiable',
417 => 'Expectation failed',
- 418 => 'I’m a teapot',
+ 418 => 'I\'m a teapot',
422 => 'Unprocessable entity',
423 => 'Locked',
424 => 'Method failure',
@@ -116,48 +204,60 @@ class Response extends \yii\base\Response
509 => 'Bandwidth Limit Exceeded',
510 => 'Not Extended',
511 => 'Network Authentication Required',
- );
+ ];
- private $_statusCode;
+ /**
+ * @var integer the HTTP status code to send with the response.
+ */
+ private $_statusCode = 200;
/**
* @var HeaderCollection
*/
private $_headers;
-
+ /**
+ * Initializes this component.
+ */
public function init()
{
+ if ($this->version === null) {
+ if (isset($_SERVER['SERVER_PROTOCOL']) && $_SERVER['SERVER_PROTOCOL'] === '1.0') {
+ $this->version = '1.0';
+ } else {
+ $this->version = '1.1';
+ }
+ }
if ($this->charset === null) {
$this->charset = Yii::$app->charset;
}
}
- public function begin()
- {
- parent::begin();
- $this->beginOutput();
- }
-
- public function end()
- {
- $this->content .= $this->endOutput();
- $this->send();
- parent::end();
- }
-
+ /**
+ * @return integer the HTTP status code to send with the response.
+ */
public function getStatusCode()
{
return $this->_statusCode;
}
+ /**
+ * Sets the response status code.
+ * This method will set the corresponding status text if `$text` is null.
+ * @param integer $value the status code
+ * @param string $text the status text. If not set, it will be set automatically based on the status code.
+ * @throws InvalidParamException if the status code is invalid.
+ */
public function setStatusCode($value, $text = null)
{
+ if ($value === null) {
+ $value = 200;
+ }
$this->_statusCode = (int)$value;
- if ($this->isInvalid()) {
+ if ($this->getIsInvalid()) {
throw new InvalidParamException("The HTTP status code is invalid: $value");
}
if ($text === null) {
- $this->statusText = isset(self::$statusTexts[$this->_statusCode]) ? self::$statusTexts[$this->_statusCode] : '';
+ $this->statusText = isset(static::$httpStatuses[$this->_statusCode]) ? static::$httpStatuses[$this->_statusCode] : '';
} else {
$this->statusText = $text;
}
@@ -176,34 +276,29 @@ class Response extends \yii\base\Response
return $this->_headers;
}
- public function renderJson($data)
- {
- $this->getHeaders()->set('content-type', 'application/json');
- $this->content = Json::encode($data);
- }
-
- public function renderJsonp($data, $callbackName)
- {
- $this->getHeaders()->set('content-type', 'text/javascript');
- $data = Json::encode($data);
- $this->content = "$callbackName($data);";
- }
-
/**
* Sends the response to the client.
- * @return boolean true if the response was sent
*/
public function send()
{
+ $this->trigger(self::EVENT_BEFORE_SEND, new ResponseEvent($this));
+ $this->prepare();
+ $this->trigger(self::EVENT_AFTER_PREPARE, new ResponseEvent($this));
$this->sendHeaders();
$this->sendContent();
+ $this->trigger(self::EVENT_AFTER_SEND, new ResponseEvent($this));
}
- public function reset()
+ /**
+ * Clears the headers, cookies, content, status code of the response.
+ */
+ public function clear()
{
$this->_headers = null;
- $this->_statusCode = null;
- $this->statusText = null;
+ $this->_cookies = null;
+ $this->_statusCode = 200;
+ $this->statusText = 'OK';
+ $this->data = null;
$this->content = null;
}
@@ -216,17 +311,15 @@ class Response extends \yii\base\Response
return;
}
$statusCode = $this->getStatusCode();
- if ($statusCode !== null) {
- header("HTTP/{$this->version} $statusCode {$this->statusText}");
- }
+ header("HTTP/{$this->version} $statusCode {$this->statusText}");
if ($this->_headers) {
$headers = $this->getHeaders();
foreach ($headers as $name => $values) {
+ $name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $name)));
foreach ($values as $value) {
header("$name: $value", false);
}
}
- $headers->removeAll();
}
$this->sendCookies();
}
@@ -246,7 +339,7 @@ class Response extends \yii\base\Response
foreach ($this->getCookies() as $cookie) {
$value = $cookie->value;
if ($cookie->expire != 1 && isset($validationKey)) {
- $value = SecurityHelper::hashData(serialize($value), $validationKey);
+ $value = Security::hashData(serialize($value), $validationKey);
}
setcookie($cookie->name, $value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly);
}
@@ -259,96 +352,150 @@ class Response extends \yii\base\Response
protected function sendContent()
{
echo $this->content;
- $this->content = null;
}
/**
- * Sends a file to user.
- * @param string $fileName file name
- * @param string $content content to be set.
- * @param string $mimeType mime type of the content. If null, it will be guessed automatically based on the given file name.
- * @param boolean $terminate whether to terminate the current application after calling this method
- * @throws \yii\base\HttpException when range request is not satisfiable.
+ * Sends a file to the browser.
+ * @param string $filePath the path of the file to be sent.
+ * @param string $attachmentName the file name shown to the user. If null, it will be determined from `$filePath`.
+ * @param string $mimeType the MIME type of the content. If null, it will be guessed based on `$filePath`
*/
- public function sendFile($fileName, $content, $mimeType = null, $terminate = true)
+ public function sendFile($filePath, $attachmentName = null, $mimeType = null)
{
- if ($mimeType === null && (($mimeType = FileHelper::getMimeTypeByExtension($fileName)) === null)) {
+ if ($mimeType === null && ($mimeType = FileHelper::getMimeTypeByExtension($filePath)) === null) {
$mimeType = 'application/octet-stream';
}
+ if ($attachmentName === null) {
+ $attachmentName = basename($filePath);
+ }
+ $handle = fopen($filePath, 'rb');
+ $this->sendStreamAsFile($handle, $attachmentName, $mimeType);
+ }
- $fileSize = StringHelper::strlen($content);
- $contentStart = 0;
- $contentEnd = $fileSize - 1;
-
+ /**
+ * Sends the specified content as a file to the browser.
+ * @param string $content the content to be sent. The existing [[content]] will be discarded.
+ * @param string $attachmentName the file name shown to the user.
+ * @param string $mimeType the MIME type of the content.
+ * @throws HttpException if the requested range is not satisfiable
+ */
+ public function sendContentAsFile($content, $attachmentName, $mimeType = 'application/octet-stream')
+ {
$headers = $this->getHeaders();
+ $contentLength = StringHelper::byteLength($content);
+ $range = $this->getHttpRange($contentLength);
+ if ($range === false) {
+ $headers->set('Content-Range', "bytes */$contentLength");
+ throw new HttpException(416, Yii::t('yii', 'Requested range not satisfiable'));
+ }
- // tell the client that we accept range requests
- $headers->set('Accept-Ranges', 'bytes');
-
- if (isset($_SERVER['HTTP_RANGE'])) {
- // client sent us a multibyte range, can not hold this one for now
- if (strpos($_SERVER['HTTP_RANGE'], ',') !== false) {
- $headers->set('Content-Range', "bytes $contentStart-$contentEnd/$fileSize");
- throw new HttpException(416, 'Requested Range Not Satisfiable');
- }
-
- $range = str_replace('bytes=', '', $_SERVER['HTTP_RANGE']);
-
- // range requests starts from "-", so it means that data must be dumped the end point.
- if ($range[0] === '-') {
- $contentStart = $fileSize - substr($range, 1);
- } else {
- $range = explode('-', $range);
- $contentStart = $range[0];
-
- // check if the last-byte-pos presents in header
- if ((isset($range[1]) && is_numeric($range[1]))) {
- $contentEnd = $range[1];
- }
- }
+ $headers->setDefault('Pragma', 'public')
+ ->setDefault('Accept-Ranges', 'bytes')
+ ->setDefault('Expires', '0')
+ ->setDefault('Content-Type', $mimeType)
+ ->setDefault('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
+ ->setDefault('Content-Transfer-Encoding', 'binary')
+ ->setDefault('Content-Length', StringHelper::byteLength($content))
+ ->setDefault('Content-Disposition', "attachment; filename=\"$attachmentName\"");
+
+ list($begin, $end) = $range;
+ if ($begin !=0 || $end != $contentLength - 1) {
+ $this->setStatusCode(206);
+ $headers->set('Content-Range', "bytes $begin-$end/$contentLength");
+ $this->content = StringHelper::byteSubstr($content, $begin, $end - $begin + 1);
+ } else {
+ $this->setStatusCode(200);
+ $this->content = $content;
+ }
- /* Check the range and make sure it's treated according to the specs.
- * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
- */
- // End bytes can not be larger than $end.
- $contentEnd = ($contentEnd > $fileSize) ? $fileSize - 1 : $contentEnd;
+ $this->format = self::FORMAT_RAW;
+ $this->send();
+ }
- // Validate the requested range and return an error if it's not correct.
- $wrongContentStart = ($contentStart > $contentEnd || $contentStart > $fileSize - 1 || $contentStart < 0);
+ /**
+ * Sends the specified stream as a file to the browser.
+ * @param resource $handle the handle of the stream to be sent.
+ * @param string $attachmentName the file name shown to the user.
+ * @param string $mimeType the MIME type of the stream content.
+ * @throws HttpException if the requested range cannot be satisfied.
+ */
+ public function sendStreamAsFile($handle, $attachmentName, $mimeType = 'application/octet-stream')
+ {
+ $headers = $this->getHeaders();
+ fseek($handle, 0, SEEK_END);
+ $fileSize = ftell($handle);
- if ($wrongContentStart) {
- $headers->set('Content-Range', "bytes $contentStart-$contentEnd/$fileSize");
- throw new HttpException(416, 'Requested Range Not Satisfiable');
- }
+ $range = $this->getHttpRange($fileSize);
+ if ($range === false) {
+ $headers->set('Content-Range', "bytes */$fileSize");
+ throw new HttpException(416, Yii::t('yii', 'Requested range not satisfiable'));
+ }
+ list($begin, $end) = $range;
+ if ($begin !=0 || $end != $fileSize - 1) {
$this->setStatusCode(206);
- $headers->set('Content-Range', "bytes $contentStart-$contentEnd/$fileSize");
+ $headers->set('Content-Range', "bytes $begin-$end/$fileSize");
} else {
$this->setStatusCode(200);
}
- $length = $contentEnd - $contentStart + 1; // Calculate new content length
-
- $headers->set('Pragma', 'public')
- ->set('Expires', '0')
- ->set('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
- ->set('Content-Type', $mimeType)
- ->set('Content-Length', $length)
- ->set('Content-Disposition', "attachment; filename=\"$fileName\"")
- ->set('Content-Transfer-Encoding', 'binary');
+ $length = $end - $begin + 1;
+
+ $headers->setDefault('Pragma', 'public')
+ ->setDefault('Accept-Ranges', 'bytes')
+ ->setDefault('Expires', '0')
+ ->setDefault('Content-Type', $mimeType)
+ ->setDefault('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
+ ->setDefault('Content-Transfer-Encoding', 'binary')
+ ->setDefault('Content-Length', $length)
+ ->setDefault('Content-Disposition', "attachment; filename=\"$attachmentName\"");
+ $this->format = self::FORMAT_RAW;
+ $this->data = $this->content = null;
+ $this->send();
- $content = StringHelper::substr($content, $contentStart, $length);
+ fseek($handle, $begin);
+ set_time_limit(0); // Reset time limit for big files
+ $chunkSize = 8 * 1024 * 1024; // 8MB per chunk
+ while (!feof($handle) && ($pos = ftell($handle)) <= $end) {
+ if ($pos + $chunkSize > $end) {
+ $chunkSize = $end - $pos + 1;
+ }
+ echo fread($handle, $chunkSize);
+ flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
+ }
+ fclose($handle);
+ }
- if ($terminate) {
- // clean up the application first because the file downloading could take long time
- // which may cause timeout of some resources (such as DB connection)
- ob_start();
- Yii::$app->end(0, false);
- ob_end_clean();
- $this->content = $content;
- exit(0);
+ /**
+ * Determines the HTTP range given in the request.
+ * @param integer $fileSize the size of the file that will be used to validate the requested HTTP range.
+ * @return array|boolean the range (begin, end), or false if the range request is invalid.
+ */
+ protected function getHttpRange($fileSize)
+ {
+ if (!isset($_SERVER['HTTP_RANGE']) || $_SERVER['HTTP_RANGE'] === '-') {
+ return [0, $fileSize - 1];
+ }
+ if (!preg_match('/^bytes=(\d*)-(\d*)$/', $_SERVER['HTTP_RANGE'], $matches)) {
+ return false;
+ }
+ if ($matches[1] === '') {
+ $start = $fileSize - $matches[2];
+ $end = $fileSize - 1;
+ } elseif ($matches[2] !== '') {
+ $start = $matches[1];
+ $end = $matches[2];
+ if ($end >= $fileSize) {
+ $end = $fileSize - 1;
+ }
} else {
- $this->content = $content;
+ $start = $matches[1];
+ $end = $fileSize - 1;
+ }
+ if ($start < 0 || $start > $end) {
+ return false;
+ } else {
+ return [$start, $end];
}
}
@@ -366,143 +513,150 @@ class Response extends \yii\base\Response
* specified by that header using web server internals including all optimizations like caching-headers.
*
* As this header directive is non-standard different directives exists for different web servers applications:
- *
- * Apache: {@link http://tn123.org/mod_xsendfile X-Sendfile}
- * Lighttpd v1.4: {@link http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file X-LIGHTTPD-send-file}
- * Lighttpd v1.5: {@link http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file X-Sendfile}
- * Nginx: {@link http://wiki.nginx.org/XSendfile X-Accel-Redirect}
- * Cherokee: {@link http://www.cherokee-project.com/doc/other_goodies.html#x-sendfile X-Sendfile and X-Accel-Redirect}
- *
+ *
+ * - Apache: [X-Sendfile](http://tn123.org/mod_xsendfile)
+ * - Lighttpd v1.4: [X-LIGHTTPD-send-file](http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)
+ * - Lighttpd v1.5: [X-Sendfile](http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)
+ * - Nginx: [X-Accel-Redirect](http://wiki.nginx.org/XSendfile)
+ * - Cherokee: [X-Sendfile and X-Accel-Redirect](http://www.cherokee-project.com/doc/other_goodies.html#x-sendfile)
+ *
* So for this method to work the X-SENDFILE option/module should be enabled by the web server and
* a proper xHeader should be sent.
*
- * Note:
- * This option allows to download files that are not under web folders, and even files that are otherwise protected (deny from all) like .htaccess
+ * **Note**
+ *
+ * This option allows to download files that are not under web folders, and even files that are otherwise protected
+ * (deny from all) like `.htaccess`.
+ *
+ * **Side effects**
*
- * Side effects :
* If this option is disabled by the web server, when this method is called a download configuration dialog
* will open but the downloaded file will have 0 bytes.
*
- * Known issues :
+ * **Known issues**
+ *
* There is a Bug with Internet Explorer 6, 7 and 8 when X-SENDFILE is used over an SSL connection, it will show
- * an error message like this: "Internet Explorer was not able to open this Internet site. The requested site is either unavailable or cannot be found.".
- * You can work around this problem by removing the Pragma
-header.
- *
- * Example :
- *
- * request->xSendFile('/home/user/Pictures/picture1.jpg', array(
- * 'saveName' => 'image1.jpg',
- * 'mimeType' => 'image/jpeg',
- * 'terminate' => false,
- * ));
- * ?>
- *
+ * an error message like this: "Internet Explorer was not able to open this Internet site. The requested site
+ * is either unavailable or cannot be found.". You can work around this problem by removing the `Pragma`-header.
+ *
+ * **Example**
+ *
+ * ~~~
+ * Yii::$app->response->xSendFile('/home/user/Pictures/picture1.jpg');
+ * ~~~
+ *
* @param string $filePath file name with full path
- * @param array $options additional options:
- *
- * saveName: file name shown to the user, if not set real file name will be used
- * mimeType: mime type of the file, if not set it will be guessed automatically based on the file name, if set to null no content-type header will be sent.
- * xHeader: appropriate x-sendfile header, defaults to "X-Sendfile"
- * terminate: whether to terminate the current application after calling this method, defaults to true
- * forceDownload: specifies whether the file will be downloaded or shown inline, defaults to true
- * addHeaders: an array of additional http headers in header-value pairs
- *
- * @todo
- */
- public function xSendFile($filePath, $options = array())
+ * @param string $mimeType the MIME type of the file. If null, it will be determined based on `$filePath`.
+ * @param string $attachmentName file name shown to the user. If null, it will be determined from `$filePath`.
+ * @param string $xHeader the name of the x-sendfile header.
+ */
+ public function xSendFile($filePath, $attachmentName = null, $mimeType = null, $xHeader = 'X-Sendfile')
{
- if (!isset($options['forceDownload']) || $options['forceDownload']) {
- $disposition = 'attachment';
- } else {
- $disposition = 'inline';
- }
-
- if (!isset($options['saveName'])) {
- $options['saveName'] = basename($filePath);
+ if ($mimeType === null && ($mimeType = FileHelper::getMimeTypeByExtension($filePath)) === null) {
+ $mimeType = 'application/octet-stream';
}
-
- if (!isset($options['mimeType'])) {
- if (($options['mimeType'] = FileHelper::getMimeTypeByExtension($filePath)) === null) {
- $options['mimeType'] = 'text/plain';
- }
+ if ($attachmentName === null) {
+ $attachmentName = basename($filePath);
}
- if (!isset($options['xHeader'])) {
- $options['xHeader'] = 'X-Sendfile';
- }
+ $this->getHeaders()
+ ->setDefault($xHeader, $filePath)
+ ->setDefault('Content-Type', $mimeType)
+ ->setDefault('Content-Disposition', "attachment; filename=\"$attachmentName\"");
- $headers = $this->getHeaders();
-
- if ($options['mimeType'] !== null) {
- $headers->set('Content-Type', $options['mimeType']);
- }
- $headers->set('Content-Disposition', "$disposition; filename=\"{$options['saveName']}\"");
- if (isset($options['addHeaders'])) {
- foreach ($options['addHeaders'] as $header => $value) {
- $headers->set($header, $value);
- }
- }
- $headers->set(trim($options['xHeader']), $filePath);
-
- if (!isset($options['terminate']) || $options['terminate']) {
- Yii::$app->end();
- }
+ $this->send();
}
/**
* Redirects the browser to the specified URL.
- * This method will send out a "Location" header to achieve the redirection.
+ *
+ * This method adds a "Location" header to the current response. Note that it does not send out
+ * the header until [[send()]] is called. In a controller action you may use this method as follows:
+ *
+ * ~~~
+ * return Yii::$app->getResponse()->redirect($url);
+ * ~~~
+ *
+ * In other places, if you want to send out the "Location" header immediately, you should use
+ * the following code:
+ *
+ * ~~~
+ * Yii::$app->getResponse()->redirect($url)->send();
+ * return;
+ * ~~~
+ *
* In AJAX mode, this normally will not work as expected unless there are some
* client-side JavaScript code handling the redirection. To help achieve this goal,
- * this method will use [[ajaxRedirectCode]] as the HTTP status code when performing
- * redirection in AJAX mode. The following JavaScript code may be used on the client
- * side to handle the redirection response:
+ * this method will send out a "X-Redirect" header instead of "Location".
+ *
+ * If you use the "yii" JavaScript module, it will handle the AJAX redirection as
+ * described above. Otherwise, you should write the following JavaScript code to
+ * handle the redirection:
*
* ~~~
- * $(document).ajaxSuccess(function(event, xhr, settings) {
- * if (xhr.status == 278) {
- * window.location = xhr.getResponseHeader('Location');
+ * $document.ajaxComplete(function (event, xhr, settings) {
+ * var url = xhr.getResponseHeader('X-Redirect');
+ * if (url) {
+ * window.location = url;
* }
* });
* ~~~
*
- * @param array|string $url the URL to be redirected to. [[\yii\helpers\Html::url()]]
- * will be used to normalize the URL. If the resulting URL is still a relative URL
- * (one without host info), the current request host info will be used.
- * @param boolean $terminate whether to terminate the current application
+ * @param string|array $url the URL to be redirected to. This can be in one of the following formats:
+ *
+ * - a string representing a URL (e.g. "http://example.com")
+ * - a string representing a URL alias (e.g. "@example.com")
+ * - an array in the format of `[$route, ...name-value pairs...]` (e.g. `['site/index', 'ref' => 1]`).
+ * Note that the route is with respect to the whole application, instead of relative to a controller or module.
+ * [[Html::url()]] will be used to convert the array into a URL.
+ *
+ * Any relative URL will be converted into an absolute one by prepending it with the host info
+ * of the current request.
+ *
* @param integer $statusCode the HTTP status code. Defaults to 302.
* See [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html]]
- * for details about HTTP status code.
- * Note that if the request is an AJAX request, [[ajaxRedirectCode]] will be used instead.
+ * for details about HTTP status code
+ * @return static the response object itself
*/
- public function redirect($url, $terminate = true, $statusCode = 302)
+ public function redirect($url, $statusCode = 302)
{
+ if (is_array($url) && isset($url[0])) {
+ // ensure the route is absolute
+ $url[0] = '/' . ltrim($url[0], '/');
+ }
$url = Html::url($url);
if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) {
$url = Yii::$app->getRequest()->getHostInfo() . $url;
}
+
if (Yii::$app->getRequest()->getIsAjax()) {
- $statusCode = $this->ajaxRedirectCode;
+ $this->getHeaders()->set('X-Redirect', $url);
+ } else {
+ $this->getHeaders()->set('Location', $url);
}
- $this->getHeaders()->set('Location', $url);
$this->setStatusCode($statusCode);
- if ($terminate) {
- Yii::$app->end();
- }
+
+ return $this;
}
/**
* Refreshes the current page.
* The effect of this method call is the same as the user pressing the refresh button of his browser
* (without re-posting data).
- * @param boolean $terminate whether to terminate the current application after calling this method
+ *
+ * In a controller action you may use this method like this:
+ *
+ * ~~~
+ * return Yii::$app->getResponse()->refresh();
+ * ~~~
+ *
* @param string $anchor the anchor that should be appended to the redirection URL.
* Defaults to empty. Make sure the anchor starts with '#' if you want to specify it.
+ * @return Response the response object itself
*/
- public function refresh($terminate = true, $anchor = '')
+ public function refresh($anchor = '')
{
- $this->redirect(Yii::$app->getRequest()->getUrl() . $anchor, $terminate);
+ return $this->redirect(Yii::$app->getRequest()->getUrl() . $anchor);
}
private $_cookies;
@@ -513,10 +667,10 @@ class Response extends \yii\base\Response
*
* ~~~
* // add a cookie
- * $response->cookies->add(new Cookie(array(
+ * $response->cookies->add(new Cookie([
* 'name' => $name,
* 'value' => $value,
- * ));
+ * ]);
*
* // remove a cookie
* $response->cookies->remove('name');
@@ -537,7 +691,7 @@ class Response extends \yii\base\Response
/**
* @return boolean whether this response has a valid [[statusCode]].
*/
- public function isInvalid()
+ public function getIsInvalid()
{
return $this->getStatusCode() < 100 || $this->getStatusCode() >= 600;
}
@@ -545,15 +699,15 @@ class Response extends \yii\base\Response
/**
* @return boolean whether this response is informational
*/
- public function isInformational()
+ public function getIsInformational()
{
return $this->getStatusCode() >= 100 && $this->getStatusCode() < 200;
}
/**
- * @return boolean whether this response is successfully
+ * @return boolean whether this response is successful
*/
- public function isSuccessful()
+ public function getIsSuccessful()
{
return $this->getStatusCode() >= 200 && $this->getStatusCode() < 300;
}
@@ -561,7 +715,7 @@ class Response extends \yii\base\Response
/**
* @return boolean whether this response is a redirection
*/
- public function isRedirection()
+ public function getIsRedirection()
{
return $this->getStatusCode() >= 300 && $this->getStatusCode() < 400;
}
@@ -569,7 +723,7 @@ class Response extends \yii\base\Response
/**
* @return boolean whether this response indicates a client error
*/
- public function isClientError()
+ public function getIsClientError()
{
return $this->getStatusCode() >= 400 && $this->getStatusCode() < 500;
}
@@ -577,7 +731,7 @@ class Response extends \yii\base\Response
/**
* @return boolean whether this response indicates a server error
*/
- public function isServerError()
+ public function getIsServerError()
{
return $this->getStatusCode() >= 500 && $this->getStatusCode() < 600;
}
@@ -585,32 +739,90 @@ class Response extends \yii\base\Response
/**
* @return boolean whether this response is OK
*/
- public function isOk()
+ public function getIsOk()
{
- return 200 === $this->getStatusCode();
+ return $this->getStatusCode() == 200;
}
/**
* @return boolean whether this response indicates the current request is forbidden
*/
- public function isForbidden()
+ public function getIsForbidden()
{
- return 403 === $this->getStatusCode();
+ return $this->getStatusCode() == 403;
}
/**
* @return boolean whether this response indicates the currently requested resource is not found
*/
- public function isNotFound()
+ public function getIsNotFound()
{
- return 404 === $this->getStatusCode();
+ return $this->getStatusCode() == 404;
}
/**
* @return boolean whether this response is empty
*/
- public function isEmpty()
+ public function getIsEmpty()
+ {
+ return in_array($this->getStatusCode(), [201, 204, 304]);
+ }
+
+ /**
+ * Prepares for sending the response.
+ * The default implementation will convert [[data]] into [[content]] and set headers accordingly.
+ * @throws InvalidConfigException if the formatter for the specified format is invalid or [[format]] is not supported
+ */
+ protected function prepare()
{
- return in_array($this->getStatusCode(), array(201, 204, 304));
+ if ($this->data === null) {
+ return;
+ }
+
+ if (isset($this->formatters[$this->format])) {
+ $formatter = $this->formatters[$this->format];
+ if (!is_object($formatter)) {
+ $formatter = Yii::createObject($formatter);
+ }
+ if ($formatter instanceof ResponseFormatterInterface) {
+ $formatter->format($this);
+ } else {
+ throw new InvalidConfigException("The '{$this->format}' response formatter is invalid. It must implement the ResponseFormatterInterface.");
+ }
+ } else {
+ switch ($this->format) {
+ case self::FORMAT_HTML:
+ $this->getHeaders()->setDefault('Content-Type', 'text/html; charset=' . $this->charset);
+ $this->content = $this->data;
+ break;
+ case self::FORMAT_RAW:
+ $this->content = $this->data;
+ break;
+ case self::FORMAT_JSON:
+ $this->getHeaders()->set('Content-Type', 'application/json');
+ $this->content = Json::encode($this->data);
+ break;
+ case self::FORMAT_JSONP:
+ $this->getHeaders()->set('Content-Type', 'text/javascript; charset=' . $this->charset);
+ if (is_array($this->data) && isset($this->data['data'], $this->data['callback'])) {
+ $this->content = sprintf('%s(%s);', $this->data['callback'], Json::encode($this->data['data']));
+ } else {
+ $this->content = '';
+ Yii::warning("The 'jsonp' response requires that the data be an array consisting of both 'data' and 'callback' elements.", __METHOD__);
+ }
+ break;
+ case self::FORMAT_XML:
+ Yii::createObject(XmlResponseFormatter::className())->format($this);
+ break;
+ default:
+ throw new InvalidConfigException("Unsupported response format: {$this->format}");
+ }
+ }
+
+ if (is_array($this->content)) {
+ $this->content = 'array()';
+ } elseif (is_object($this->content)) {
+ $this->content = method_exists($this->content, '__toString') ? $this->content->__toString() : get_class($this->content);
+ }
}
}
diff --git a/framework/yii/web/ResponseEvent.php b/framework/yii/web/ResponseEvent.php
new file mode 100644
index 0000000..dabaf2f
--- /dev/null
+++ b/framework/yii/web/ResponseEvent.php
@@ -0,0 +1,39 @@
+
+ * @since 2.0
+ */
+class ResponseEvent extends Event
+{
+ /**
+ * @var Response the response object associated with this event.
+ */
+ public $response;
+
+ /**
+ * Constructor.
+ * @param Response $response the response object associated with this event.
+ * @param array $config the configuration array for initializing the newly created object.
+ */
+ public function __construct($response, $config = [])
+ {
+ $this->response = $response;
+ parent::__construct($config);
+ }
+}
diff --git a/framework/yii/web/ResponseFormatterInterface.php b/framework/yii/web/ResponseFormatterInterface.php
new file mode 100644
index 0000000..689ee1e
--- /dev/null
+++ b/framework/yii/web/ResponseFormatterInterface.php
@@ -0,0 +1,23 @@
+
+ * @since 2.0
+ */
+interface ResponseFormatterInterface
+{
+ /**
+ * Formats the specified response.
+ * @param Response $response the response to be formatted.
+ */
+ public function format($response);
+}
diff --git a/framework/yii/web/Session.php b/framework/yii/web/Session.php
index cf1fa21..903e9d9 100644
--- a/framework/yii/web/Session.php
+++ b/framework/yii/web/Session.php
@@ -15,7 +15,7 @@ use yii\base\InvalidParamException;
* Session provides session data management and the related configurations.
*
* Session is a Web application component that can be accessed via `Yii::$app->session`.
-
+ *
* To start the session, call [[open()]]; To complete and send out session data, call [[close()]];
* To destroy the session, call [[destroy()]].
*
@@ -45,6 +45,28 @@ use yii\base\InvalidParamException;
* useful for displaying confirmation messages. To use flash messages, simply
* call methods such as [[setFlash()]], [[getFlash()]].
*
+ * @property array $allFlashes Flash messages (key => message). This property is read-only.
+ * @property array $cookieParams The session cookie parameters. This property is read-only.
+ * @property integer $count The number of session variables. This property is read-only.
+ * @property string $flash The key identifying the flash message. Note that flash messages and normal session
+ * variables share the same name space. If you have a normal session variable using the same name, its value will
+ * be overwritten by this method. This property is write-only.
+ * @property float $gCProbability The probability (percentage) that the GC (garbage collection) process is
+ * started on every session initialization, defaults to 1 meaning 1% chance.
+ * @property string $id The current session ID.
+ * @property boolean $isActive Whether the session has started. This property is read-only.
+ * @property SessionIterator $iterator An iterator for traversing the session variables. This property is
+ * read-only.
+ * @property string $name The current session name.
+ * @property string $savePath The current session save path, defaults to '/tmp'.
+ * @property integer $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up.
+ * The default value is 1440 seconds (or the value of "session.gc_maxlifetime" set in php.ini).
+ * @property boolean|null $useCookies The value indicating whether cookies should be used to store session
+ * IDs.
+ * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
+ * @property boolean $useTransparentSessionID Whether transparent sid support is enabled or not, defaults to
+ * false.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -58,13 +80,12 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
* @var string the name of the session variable that stores the flash message data.
*/
public $flashVar = '__flash';
-
/**
- * @var array parameter-value pairs to override default session cookie parameters
+ * @var array parameter-value pairs to override default session cookie parameters that are used for session_set_cookie_params() function
+ * Array may have the following possible keys: 'lifetime', 'path', 'domain', 'secure', 'httpOnly'
+ * @see http://www.php.net/manual/en/function.session-set-cookie-params.php
*/
- public $cookieParams = array(
- 'httpOnly' => true
- );
+ private $_cookieParams = ['httpOnly' => true];
/**
* Initializes the application component.
@@ -76,7 +97,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
if ($this->autoStart) {
$this->open();
}
- register_shutdown_function(array($this, 'close'));
+ register_shutdown_function([$this, 'close']);
}
/**
@@ -91,46 +112,36 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
return false;
}
- private $_opened = false;
-
/**
* Starts the session.
*/
public function open()
{
- // this is available in PHP 5.4.0+
- if (function_exists('session_status')) {
- if (session_status() == PHP_SESSION_ACTIVE) {
- $this->_opened = true;
- return;
- }
+ if (session_status() == PHP_SESSION_ACTIVE) {
+ return;
}
- if (!$this->_opened) {
- if ($this->getUseCustomStorage()) {
- @session_set_save_handler(
- array($this, 'openSession'),
- array($this, 'closeSession'),
- array($this, 'readSession'),
- array($this, 'writeSession'),
- array($this, 'destroySession'),
- array($this, 'gcSession')
- );
- }
+ if ($this->getUseCustomStorage()) {
+ @session_set_save_handler(
+ [$this, 'openSession'],
+ [$this, 'closeSession'],
+ [$this, 'readSession'],
+ [$this, 'writeSession'],
+ [$this, 'destroySession'],
+ [$this, 'gcSession']
+ );
+ }
- $this->setCookieParams($this->cookieParams);
+ $this->setCookieParamsInternal();
- @session_start();
+ @session_start();
- if (session_id() == '') {
- $this->_opened = false;
- $error = error_get_last();
- $message = isset($error['message']) ? $error['message'] : 'Failed to start session.';
- Yii::error($message, __METHOD__);
- } else {
- $this->_opened = true;
- $this->updateFlashCounters();
- }
+ if (session_id() == '') {
+ $error = error_get_last();
+ $message = isset($error['message']) ? $error['message'] : 'Failed to start session.';
+ Yii::error($message, __METHOD__);
+ } else {
+ $this->updateFlashCounters();
}
}
@@ -139,7 +150,6 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/
public function close()
{
- $this->_opened = false;
if (session_id() !== '') {
@session_write_close();
}
@@ -161,13 +171,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/
public function getIsActive()
{
- if (function_exists('session_status')) {
- // available in PHP 5.4.0+
- return session_status() == PHP_SESSION_ACTIVE;
- } else {
- // this is not very reliable
- return $this->_opened && session_id() !== '';
- }
+ return session_status() == PHP_SESSION_ACTIVE;
}
/**
@@ -246,26 +250,36 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
$params['httpOnly'] = $params['httponly'];
unset($params['httponly']);
}
- return $params;
+ return array_merge($params, $this->_cookieParams);
}
/**
* Sets the session cookie parameters.
- * The effect of this method only lasts for the duration of the script.
- * Call this method before the session starts.
+ * The cookie parameters passed to this method will be merged with the result
+ * of `session_get_cookie_params()`.
* @param array $value cookie parameters, valid keys include: `lifetime`, `path`, `domain`, `secure` and `httpOnly`.
* @throws InvalidParamException if the parameters are incomplete.
* @see http://us2.php.net/manual/en/function.session-set-cookie-params.php
*/
- public function setCookieParams($value)
+ public function setCookieParams(array $value)
+ {
+ $this->_cookieParams = $value;
+ }
+
+ /**
+ * Sets the session cookie parameters.
+ * This method is called by [[open()]] when it is about to open the session.
+ * @throws InvalidParamException if the parameters are incomplete.
+ * @see http://us2.php.net/manual/en/function.session-set-cookie-params.php
+ */
+ private function setCookieParamsInternal()
{
$data = $this->getCookieParams();
extract($data);
- extract($value);
if (isset($lifetime, $path, $domain, $secure, $httpOnly)) {
session_set_cookie_params($lifetime, $path, $domain, $secure, $httpOnly);
} else {
- throw new InvalidParamException('Please make sure these parameters are provided: lifetime, path, domain, secure and httpOnly.');
+ throw new InvalidParamException('Please make sure cookieParams contains these elements: lifetime, path, domain, secure and httpOnly.');
}
}
@@ -539,7 +553,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/
protected function updateFlashCounters()
{
- $counters = $this->get($this->flashVar, array());
+ $counters = $this->get($this->flashVar, []);
if (is_array($counters)) {
foreach ($counters as $key => $count) {
if ($count) {
@@ -560,12 +574,22 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
* A flash message is available only in the current request and the next request.
* @param string $key the key identifying the flash message
* @param mixed $defaultValue value to be returned if the flash message does not exist.
+ * @param boolean $delete whether to delete this flash message right after this method is called.
+ * If false, the flash message will be automatically deleted after the next request.
* @return mixed the flash message
*/
- public function getFlash($key, $defaultValue = null)
+ public function getFlash($key, $defaultValue = null, $delete = false)
{
- $counters = $this->get($this->flashVar, array());
- return isset($counters[$key]) ? $this->get($key, $defaultValue) : $defaultValue;
+ $counters = $this->get($this->flashVar, []);
+ if (isset($counters[$key])) {
+ $value = $this->get($key, $defaultValue);
+ if ($delete) {
+ $this->removeFlash($key);
+ }
+ return $value;
+ } else {
+ return $defaultValue;
+ }
}
/**
@@ -574,8 +598,8 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/
public function getAllFlashes()
{
- $counters = $this->get($this->flashVar, array());
- $flashes = array();
+ $counters = $this->get($this->flashVar, []);
+ $flashes = [];
foreach (array_keys($counters) as $key) {
if (isset($_SESSION[$key])) {
$flashes[$key] = $_SESSION[$key];
@@ -594,7 +618,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/
public function setFlash($key, $value = true)
{
- $counters = $this->get($this->flashVar, array());
+ $counters = $this->get($this->flashVar, []);
$counters[$key] = 0;
$_SESSION[$key] = $value;
$_SESSION[$this->flashVar] = $counters;
@@ -610,7 +634,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/
public function removeFlash($key)
{
- $counters = $this->get($this->flashVar, array());
+ $counters = $this->get($this->flashVar, []);
$value = isset($_SESSION[$key], $counters[$key]) ? $_SESSION[$key] : null;
unset($counters[$key], $_SESSION[$key]);
$_SESSION[$this->flashVar] = $counters;
@@ -625,7 +649,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/
public function removeAllFlashes()
{
- $counters = $this->get($this->flashVar, array());
+ $counters = $this->get($this->flashVar, []);
foreach (array_keys($counters) as $key) {
unset($_SESSION[$key]);
}
diff --git a/framework/yii/web/SpicyRice.md b/framework/yii/web/SpicyRice.md
deleted file mode 100644
index d99f3dc..0000000
--- a/framework/yii/web/SpicyRice.md
+++ /dev/null
@@ -1,11 +0,0 @@
-## Spicy Rice font
-
-* **Author:** Brian J. Bonislawsky, Astigmatic (AOETI, Astigmatic One Eye Typographic Institute)
-* **License:** SIL Open Font License (OFL), version 1.1, [notes and FAQ](http://scripts.sil.org/OFL)
-
-## Links
-
-* [Astigmatic](http://www.astigmatic.com/)
-* [Google WebFonts](http://www.google.com/webfonts/specimen/Spicy+Rice)
-* [fontsquirrel.com](http://www.fontsquirrel.com/fonts/spicy-rice)
-* [fontspace.com](http://www.fontspace.com/astigmatic-one-eye-typographic-institute/spicy-rice)
diff --git a/framework/yii/web/UploadedFile.php b/framework/yii/web/UploadedFile.php
index a1cd735..1de4d46 100644
--- a/framework/yii/web/UploadedFile.php
+++ b/framework/yii/web/UploadedFile.php
@@ -7,41 +7,53 @@
namespace yii\web;
+use yii\base\Object;
use yii\helpers\Html;
/**
+ * UploadedFile represents the information for an uploaded file.
+ *
+ * You can call [[getInstance()]] to retrieve the instance of an uploaded file,
+ * and then use [[saveAs()]] to save it on the server.
+ * You may also query other information about the file, including [[name]],
+ * [[tempName]], [[type]], [[size]] and [[error]].
+ *
+ * @property boolean $hasError Whether there is an error with the uploaded file. Check [[error]] for detailed
+ * error code information. This property is read-only.
+ *
* @author Qiang Xue
* @since 2.0
*/
-class UploadedFile extends \yii\base\Object
+class UploadedFile extends Object
{
private static $_files;
- private $_name;
- private $_tempName;
- private $_type;
- private $_size;
- private $_error;
-
/**
- * Constructor.
- * Instead of using the constructor to create a new instance,
- * you should normally call [[getInstance()]] or [[getInstances()]]
- * to obtain new instances.
- * @param string $name the original name of the file being uploaded
- * @param string $tempName the path of the uploaded file on the server.
- * @param string $type the MIME-type of the uploaded file (such as "image/gif").
- * @param integer $size the actual size of the uploaded file in bytes
- * @param integer $error the error code
+ * @var string the original name of the file being uploaded
*/
- public function __construct($name, $tempName, $type, $size, $error)
- {
- $this->_name = $name;
- $this->_tempName = $tempName;
- $this->_type = $type;
- $this->_size = $size;
- $this->_error = $error;
- }
+ public $name;
+ /**
+ * @var string the path of the uploaded file on the server.
+ * Note, this is a temporary file which will be automatically deleted by PHP
+ * after the current request is processed.
+ */
+ public $tempName;
+ /**
+ * @var string the MIME-type of the uploaded file (such as "image/gif").
+ * Since this MIME type is not checked on the server side, do not take this value for granted.
+ * Instead, use [[FileHelper::getMimeType()]] to determine the exact MIME type.
+ */
+ public $type;
+ /**
+ * @var integer the actual size of the uploaded file in bytes
+ */
+ public $size;
+ /**
+ * @var integer an error code describing the status of this file uploading.
+ * @see http://www.php.net/manual/en/features.file-upload.errors.php
+ */
+ public $error;
+
/**
* String output.
@@ -51,7 +63,7 @@ class UploadedFile extends \yii\base\Object
*/
public function __toString()
{
- return $this->_name;
+ return $this->name;
}
/**
@@ -62,7 +74,7 @@ class UploadedFile extends \yii\base\Object
* For example, '[1]file' for tabular file uploading; and 'file[1]' for an element in a file array.
* @return UploadedFile the instance of the uploaded file.
* Null is returned if no file is uploaded for the specified model attribute.
- * @see getInstanceByName
+ * @see getInstanceByName()
*/
public static function getInstance($model, $attribute)
{
@@ -110,9 +122,9 @@ class UploadedFile extends \yii\base\Object
{
$files = static::loadFiles();
if (isset($files[$name])) {
- return array($files[$name]);
+ return [$files[$name]];
}
- $results = array();
+ $results = [];
foreach ($files as $key => $file) {
if (strpos($key, "{$name}[") === 0) {
$results[] = self::$_files[$key];
@@ -142,69 +154,23 @@ class UploadedFile extends \yii\base\Object
*/
public function saveAs($file, $deleteTempFile = true)
{
- if ($this->_error == UPLOAD_ERR_OK) {
+ if ($this->error == UPLOAD_ERR_OK) {
if ($deleteTempFile) {
- return move_uploaded_file($this->_tempName, $file);
- } elseif (is_uploaded_file($this->_tempName)) {
- return copy($this->_tempName, $file);
+ return move_uploaded_file($this->tempName, $file);
+ } elseif (is_uploaded_file($this->tempName)) {
+ return copy($this->tempName, $file);
}
}
return false;
}
/**
- * @return string the original name of the file being uploaded
- */
- public function getName()
- {
- return $this->_name;
- }
-
- /**
- * @return string the path of the uploaded file on the server.
- * Note, this is a temporary file which will be automatically deleted by PHP
- * after the current request is processed.
- */
- public function getTempName()
- {
- return $this->_tempName;
- }
-
- /**
- * @return string the MIME-type of the uploaded file (such as "image/gif").
- * Since this MIME type is not checked on the server side, do not take this value for granted.
- * Instead, use [[FileHelper::getMimeType()]] to determine the exact MIME type.
- */
- public function getType()
- {
- return $this->_type;
- }
-
- /**
- * @return integer the actual size of the uploaded file in bytes
- */
- public function getSize()
- {
- return $this->_size;
- }
-
- /**
- * Returns an error code describing the status of this file uploading.
- * @return integer the error code
- * @see http://www.php.net/manual/en/features.file-upload.errors.php
- */
- public function getError()
- {
- return $this->_error;
- }
-
- /**
* @return boolean whether there is an error with the uploaded file.
* Check [[error]] for detailed error code information.
*/
public function getHasError()
{
- return $this->_error != UPLOAD_ERR_OK;
+ return $this->error != UPLOAD_ERR_OK;
}
/**
@@ -214,7 +180,7 @@ class UploadedFile extends \yii\base\Object
private static function loadFiles()
{
if (self::$_files === null) {
- self::$_files = array();
+ self::$_files = [];
if (isset($_FILES) && is_array($_FILES)) {
foreach ($_FILES as $class => $info) {
self::loadFilesRecursive($class, $info['name'], $info['tmp_name'], $info['type'], $info['size'], $info['error']);
@@ -240,7 +206,13 @@ class UploadedFile extends \yii\base\Object
self::loadFilesRecursive($key . '[' . $i . ']', $name, $tempNames[$i], $types[$i], $sizes[$i], $errors[$i]);
}
} else {
- self::$_files[$key] = new self($names, $tempNames, $types, $sizes, $errors);
+ self::$_files[$key] = new static([
+ 'name' => $names,
+ 'tempName' => $tempNames,
+ 'type' => $types,
+ 'size' => $sizes,
+ 'error' => $errors,
+ ]);
}
}
}
diff --git a/framework/yii/web/UrlManager.php b/framework/yii/web/UrlManager.php
index 4478a6c..a2044cb 100644
--- a/framework/yii/web/UrlManager.php
+++ b/framework/yii/web/UrlManager.php
@@ -14,6 +14,26 @@ use yii\caching\Cache;
/**
* UrlManager handles HTTP request parsing and creation of URLs based on a set of rules.
*
+ * UrlManager is configured as an application component in [[yii\base\Application]] by default.
+ * You can access that instance via `Yii::$app->urlManager`.
+ *
+ * You can modify its configuration by adding an array to your application config under `components`
+ * as it is shown in the following example:
+ *
+ * ~~~
+ * 'urlManager' => [
+ * 'enablePrettyUrl' => true,
+ * 'rules' => [
+ * // your rules go here
+ * ],
+ * // ...
+ * ]
+ * ~~~
+ *
+ * @property string $baseUrl The base URL that is used by [[createUrl()]] to prepend URLs it creates.
+ * @property string $hostInfo The host info (e.g. "http://www.example.com") that is used by
+ * [[createAbsoluteUrl()]] to prepend URLs it creates.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -29,6 +49,7 @@ class UrlManager extends Component
/**
* @var boolean whether to enable strict parsing. If strict parsing is enabled, the incoming
* requested URL must match at least one of the [[rules]] in order to be treated as a valid request.
+ * Otherwise, the path info part of the request will be treated as the requested route.
* This property is used only when [[enablePrettyUrl]] is true.
*/
public $enableStrictParsing = false;
@@ -49,14 +70,14 @@ class UrlManager extends Component
* For example, `'PUT post/' => 'post/update'`.
* You may specify multiple verbs by separating them with comma
* like this: `'POST,PUT post/index' => 'post/create'`.
- * The supported verbs in the shortcut format are: GET, HEAD, POST, PUT and DELETE.
+ * The supported verbs in the shortcut format are: GET, HEAD, POST, PUT, PATCH and DELETE.
* Note that [[UrlRule::mode|mode]] will be set to PARSING_ONLY when specifying verb in this way
* so you normally would not specify a verb for normal GET request.
*
* Here is an example configuration for RESTful CRUD controller:
*
* ~~~php
- * array(
+ * [
* 'dashboard' => 'site/index',
*
* 'POST s' => '/create',
@@ -65,13 +86,13 @@ class UrlManager extends Component
* 'PUT /' => '/update',
* 'DELETE /' => '/delete',
* '/' => '/view',
- * );
+ * ];
* ~~~
*
* Note that if you modify this property after the UrlManager object is created, make sure
* you populate the array with rule objects instead of rule configurations.
*/
- public $rules = array();
+ public $rules = [];
/**
* @var string the URL suffix used when in 'path' format.
* For example, ".html" can be used so that the URL looks like pointing to a static HTML page.
@@ -82,7 +103,7 @@ class UrlManager extends Component
* @var boolean whether to show entry script name in the constructed URL. Defaults to true.
* This property is used only if [[enablePrettyUrl]] is true.
*/
- public $showScriptName = false;
+ public $showScriptName = true;
/**
* @var string the GET variable name for route. This property is used only if [[enablePrettyUrl]] is false.
*/
@@ -100,9 +121,7 @@ class UrlManager extends Component
* @var array the default configuration of URL rules. Individual rule configurations
* specified via [[rules]] will take precedence when the same property of the rule is configured.
*/
- public $ruleConfig = array(
- 'class' => 'yii\web\UrlRule',
- );
+ public $ruleConfig = ['class' => 'yii\web\UrlRule'];
private $_baseUrl;
private $_hostInfo;
@@ -136,13 +155,11 @@ class UrlManager extends Component
}
}
- $rules = array();
+ $rules = [];
foreach ($this->rules as $key => $rule) {
if (!is_array($rule)) {
- $rule = array(
- 'route' => $rule,
- );
- if (preg_match('/^((?:(GET|HEAD|POST|PUT|DELETE),)*(GET|HEAD|POST|PUT|DELETE))\s+(.*)$/', $key, $matches)) {
+ $rule = ['route' => $rule];
+ if (preg_match('/^((?:(GET|HEAD|POST|PUT|PATCH|DELETE),)*(GET|HEAD|POST|PUT|PATCH|DELETE))\s+(.*)$/', $key, $matches)) {
$rule['verb'] = explode(',', $matches[1]);
$rule['mode'] = UrlRule::PARSING_ONLY;
$key = $matches[4];
@@ -154,7 +171,7 @@ class UrlManager extends Component
$this->rules = $rules;
if (isset($key, $hash)) {
- $this->cache->set($key, array($this->rules, $hash));
+ $this->cache->set($key, [$this->rules, $hash]);
}
}
@@ -167,10 +184,11 @@ class UrlManager extends Component
public function parseRequest($request)
{
if ($this->enablePrettyUrl) {
- $pathInfo = $request->pathInfo;
- /** @var $rule UrlRule */
+ $pathInfo = $request->getPathInfo();
+ /** @var UrlRule $rule */
foreach ($this->rules as $rule) {
if (($result = $rule->parseRequest($this, $request)) !== false) {
+ Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__);
return $result;
}
}
@@ -179,8 +197,10 @@ class UrlManager extends Component
return false;
}
+ Yii::trace('No matching URL rules. Using default URL parsing logic.', __METHOD__);
+
$suffix = (string)$this->suffix;
- if ($suffix !== '' && $suffix !== '/' && $pathInfo !== '') {
+ if ($suffix !== '' && $pathInfo !== '') {
$n = strlen($this->suffix);
if (substr($pathInfo, -$n) === $this->suffix) {
$pathInfo = substr($pathInfo, 0, -$n);
@@ -194,13 +214,14 @@ class UrlManager extends Component
}
}
- return array($pathInfo, array());
+ return [$pathInfo, []];
} else {
+ Yii::trace('Pretty URL not enabled. Using default URL parsing logic.', __METHOD__);
$route = $request->get($this->routeVar);
if (is_array($route)) {
$route = '';
}
- return array((string)$route, array());
+ return [(string)$route, []];
}
}
@@ -211,7 +232,7 @@ class UrlManager extends Component
* @param array $params the parameters (name-value pairs)
* @return string the created URL
*/
- public function createUrl($route, $params = array())
+ public function createUrl($route, $params = [])
{
$anchor = isset($params['#']) ? '#' . $params['#'] : '';
unset($params['#'], $params[$this->routeVar]);
@@ -220,7 +241,7 @@ class UrlManager extends Component
$baseUrl = $this->getBaseUrl();
if ($this->enablePrettyUrl) {
- /** @var $rule UrlRule */
+ /** @var UrlRule $rule */
foreach ($this->rules as $rule) {
if (($url = $rule->createUrl($this, $route, $params)) !== false) {
if ($rule->host !== null) {
@@ -247,7 +268,7 @@ class UrlManager extends Component
if (!empty($params)) {
$url .= '&' . http_build_query($params);
}
- return $url;
+ return $url . $anchor;
}
}
@@ -259,7 +280,7 @@ class UrlManager extends Component
* @return string the created URL
* @see createUrl()
*/
- public function createAbsoluteUrl($route, $params = array())
+ public function createAbsoluteUrl($route, $params = [])
{
$url = $this->createUrl($route, $params);
if (strpos($url, '://') !== false) {
@@ -278,7 +299,7 @@ class UrlManager extends Component
public function getBaseUrl()
{
if ($this->_baseUrl === null) {
- /** @var $request \yii\web\Request */
+ /** @var \yii\web\Request $request */
$request = Yii::$app->getRequest();
$this->_baseUrl = $this->showScriptName || !$this->enablePrettyUrl ? $request->getScriptUrl() : $request->getBaseUrl();
}
diff --git a/framework/yii/web/UrlRule.php b/framework/yii/web/UrlRule.php
index fc19ea3..2934b26 100644
--- a/framework/yii/web/UrlRule.php
+++ b/framework/yii/web/UrlRule.php
@@ -11,7 +11,17 @@ use yii\base\Object;
use yii\base\InvalidConfigException;
/**
- * UrlRule represents a rule used for parsing and generating URLs.
+ * UrlRule represents a rule used by [[UrlManager]] for parsing and generating URLs.
+ *
+ * To define your own URL parsing and creation logic you can extend from this class
+ * and add it to [[UrlManager::rules]] like this:
+ *
+ * ~~~
+ * 'rules' => [
+ * ['class' => 'MyUrlRule', 'pattern' => '...', 'route' => 'site/index', ...],
+ * // ...
+ * ]
+ * ~~~
*
* @author Qiang Xue
* @since 2.0
@@ -28,6 +38,10 @@ class UrlRule extends Object
const CREATION_ONLY = 2;
/**
+ * @var string the name of this rule. If not set, it will use [[pattern]] as the name.
+ */
+ public $name;
+ /**
* @var string the pattern used to parse and create the path info part of a URL.
* @see host
*/
@@ -46,7 +60,7 @@ class UrlRule extends Object
* When this rule is used to parse the incoming request, the values declared in this property
* will be injected into $_GET.
*/
- public $defaults = array();
+ public $defaults = [];
/**
* @var string the URL suffix used for this rule.
* For example, ".html" can be used so that the URL looks like pointing to a static HTML page.
@@ -80,11 +94,11 @@ class UrlRule extends Object
/**
* @var array list of regex for matching parameters. This is used in generating URL.
*/
- private $_paramRules = array();
+ private $_paramRules = [];
/**
* @var array list of parameters used in the route.
*/
- private $_routeParams = array();
+ private $_routeParams = [];
/**
* Initializes this rule.
@@ -103,9 +117,12 @@ class UrlRule extends Object
$this->verb[$i] = strtoupper($verb);
}
} else {
- $this->verb = array(strtoupper($this->verb));
+ $this->verb = [strtoupper($this->verb)];
}
}
+ if ($this->name === null) {
+ $this->name = $this->pattern;
+ }
$this->pattern = trim($this->pattern, '/');
@@ -126,7 +143,16 @@ class UrlRule extends Object
}
}
- $tr = $tr2 = array();
+ $tr = [
+ '.' => '\\.',
+ '*' => '\\*',
+ '$' => '\\$',
+ '[' => '\\[',
+ ']' => '\\]',
+ '(' => '\\(',
+ ')' => '\\)',
+ ];
+ $tr2 = [];
if (preg_match_all('/<(\w+):?([^>]+)?>/', $this->pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
foreach ($matches as $match) {
$name = $match[1][0];
@@ -185,8 +211,7 @@ class UrlRule extends Object
// suffix alone is not allowed
return false;
}
- } elseif ($suffix !== '/') {
- // we allow the ending '/' to be optional if it is a suffix
+ } else {
return false;
}
}
@@ -204,7 +229,7 @@ class UrlRule extends Object
}
}
$params = $this->defaults;
- $tr = array();
+ $tr = [];
foreach ($matches as $name => $value) {
if (isset($this->_routeParams[$name])) {
$tr[$this->_routeParams[$name]] = $value;
@@ -218,7 +243,7 @@ class UrlRule extends Object
} else {
$route = $this->route;
}
- return array($route, $params);
+ return [$route, $params];
}
/**
@@ -234,7 +259,7 @@ class UrlRule extends Object
return false;
}
- $tr = array();
+ $tr = [];
// match the route part first
if ($route !== $this->route) {
@@ -271,7 +296,7 @@ class UrlRule extends Object
// match params in the pattern
foreach ($this->_paramRules as $name => $rule) {
- if (isset($params[$name]) && ($rule === '' || preg_match($rule, $params[$name]))) {
+ if (isset($params[$name]) && !is_array($params[$name]) && ($rule === '' || preg_match($rule, $params[$name]))) {
$tr["<$name>"] = urlencode($params[$name]);
unset($params[$name]);
} elseif (!isset($this->defaults[$name]) || isset($params[$name])) {
diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php
index 7ea561c..b640756 100644
--- a/framework/yii/web/User.php
+++ b/framework/yii/web/User.php
@@ -9,7 +9,6 @@ namespace yii\web;
use Yii;
use yii\base\Component;
-use yii\base\HttpException;
use yii\base\InvalidConfigException;
/**
@@ -18,9 +17,32 @@ use yii\base\InvalidConfigException;
* In particular, [[User::isGuest]] returns a value indicating whether the current user is a guest or not.
* Through methods [[login()]] and [[logout()]], you can change the user authentication status.
*
- * User works with a class implementing the [[Identity]] interface. This class implements
+ * User works with a class implementing the [[IdentityInterface]]. This class implements
* the actual user authentication logic and is often backed by a user database table.
*
+ * User is configured as an application component in [[yii\web\Application]] by default.
+ * You can access that instance via `Yii::$app->user`.
+ *
+ * You can modify its configuration by adding an array to your application config under `components`
+ * as it is shown in the following example:
+ *
+ * ~~~
+ * 'user' => [
+ * 'identityClass' => 'app\models\User', // User must implement the IdentityInterface
+ * 'enableAutoLogin' => true,
+ * // 'loginUrl' => ['user/login'],
+ * // ...
+ * ]
+ * ~~~
+ *
+ * @property string|integer $id The unique identifier for the user. If null, it means the user is a guest.
+ * This property is read-only.
+ * @property IdentityInterface $identity The identity object associated with the currently logged user. Null
+ * is returned if the user is not logged in (not authenticated).
+ * @property boolean $isGuest Whether the current user is a guest. This property is read-only.
+ * @property string $returnUrl The URL that the user should be redirected to after login. Note that the type
+ * of this property differs in getter and setter. See [[getReturnUrl()]] and [[setReturnUrl()]] for details.
+ *
* @author Qiang Xue
* @since 2.0
*/
@@ -46,17 +68,17 @@ class User extends Component
* the name-value pairs are GET parameters used to construct the login URL. For example,
*
* ~~~
- * array('site/login', 'ref' => 1)
+ * ['site/login', 'ref' => 1]
* ~~~
*
* If this property is null, a 403 HTTP exception will be raised when [[loginRequired()]] is called.
*/
- public $loginUrl = array('site/login');
+ public $loginUrl = ['site/login'];
/**
* @var array the configuration of the identity cookie. This property is used only when [[enableAutoLogin]] is true.
* @see Cookie
*/
- public $identityCookie = array('name' => '_identity', 'httpOnly' => true);
+ public $identityCookie = ['name' => '_identity', 'httpOnly' => true];
/**
* @var integer the number of seconds in which the user will be logged out automatically if he
* remains inactive. If this property is not set, the user will be logged out after
@@ -86,7 +108,7 @@ class User extends Component
*/
public $returnUrlVar = '__returnUrl';
- private $_access = array();
+ private $_access = [];
/**
@@ -120,10 +142,10 @@ class User extends Component
/**
* Returns the identity object associated with the currently logged user.
- * @return Identity the identity object associated with the currently logged user.
+ * @return IdentityInterface the identity object associated with the currently logged user.
* Null is returned if the user is not logged in (not authenticated).
- * @see login
- * @see logout
+ * @see login()
+ * @see logout()
*/
public function getIdentity()
{
@@ -132,8 +154,8 @@ class User extends Component
if ($id === null) {
$this->_identity = null;
} else {
- /** @var $class Identity */
- $class = Yii::import($this->identityClass);
+ /** @var IdentityInterface $class */
+ $class = $this->identityClass;
$this->_identity = $class::findIdentity($id);
}
}
@@ -148,7 +170,7 @@ class User extends Component
* You should normally update the user identity via methods [[login()]], [[logout()]]
* or [[switchIdentity()]].
*
- * @param Identity $identity the identity object associated with the currently logged user.
+ * @param IdentityInterface $identity the identity object associated with the currently logged user.
*/
public function setIdentity($identity)
{
@@ -163,7 +185,7 @@ class User extends Component
* and [[enableAutoLogin]] is true, it will also send out an identity
* cookie to support cookie-based login.
*
- * @param Identity $identity the user identity (which should already be authenticated)
+ * @param IdentityInterface $identity the user identity (which should already be authenticated)
* @param integer $duration number of seconds that the user can remain in logged-in status.
* Defaults to 0, meaning login till the user closes the browser or the session is manually destroyed.
* If greater than 0 and [[enableAutoLogin]] is true, cookie-based login will be supported.
@@ -173,6 +195,9 @@ class User extends Component
{
if ($this->beforeLogin($identity, false)) {
$this->switchIdentity($identity, $duration);
+ $id = $identity->getId();
+ $ip = Yii::$app->getRequest()->getUserIP();
+ Yii::info("User '$id' logged in from $ip.", __METHOD__);
$this->afterLogin($identity, false);
}
return !$this->getIsGuest();
@@ -192,12 +217,14 @@ class User extends Component
$data = json_decode($value, true);
if (count($data) === 3 && isset($data[0], $data[1], $data[2])) {
list ($id, $authKey, $duration) = $data;
- /** @var $class Identity */
+ /** @var IdentityInterface $class */
$class = $this->identityClass;
$identity = $class::findIdentity($id);
if ($identity !== null && $identity->validateAuthKey($authKey)) {
if ($this->beforeLogin($identity, true)) {
$this->switchIdentity($identity, $this->autoRenewCookie ? $duration : 0);
+ $ip = Yii::$app->getRequest()->getUserIP();
+ Yii::info("User '$id' logged in from $ip via cookie.", __METHOD__);
$this->afterLogin($identity, true);
}
} elseif ($identity !== null) {
@@ -218,6 +245,9 @@ class User extends Component
$identity = $this->getIdentity();
if ($identity !== null && $this->beforeLogout($identity)) {
$this->switchIdentity(null);
+ $id = $identity->getId();
+ $ip = Yii::$app->getRequest()->getUserIP();
+ Yii::info("User '$id' logged out from $ip.", __METHOD__);
if ($destroySession) {
Yii::$app->getSession()->destroy();
}
@@ -248,20 +278,34 @@ class User extends Component
* This property is usually used by the login action. If the login is successful,
* the action should read this property and use it to redirect the user browser.
* @param string|array $defaultUrl the default return URL in case it was not set previously.
- * If this is null, it means [[Application::homeUrl]] will be redirected to.
- * Please refer to [[\yii\helpers\Html::url()]] on acceptable URL formats.
+ * If this is null and the return URL was not set previously, [[Application::homeUrl]] will be redirected to.
+ * Please refer to [[setReturnUrl()]] on accepted format of the URL.
* @return string the URL that the user should be redirected to after login.
- * @see loginRequired
+ * @see loginRequired()
*/
public function getReturnUrl($defaultUrl = null)
{
$url = Yii::$app->getSession()->get($this->returnUrlVar, $defaultUrl);
+ if (is_array($url)) {
+ if (isset($url[0])) {
+ $route = array_shift($url);
+ return Yii::$app->getUrlManager()->createUrl($route, $url);
+ } else {
+ $url = null;
+ }
+ }
return $url === null ? Yii::$app->getHomeUrl() : $url;
}
/**
* @param string|array $url the URL that the user should be redirected to after login.
- * Please refer to [[\yii\helpers\Html::url()]] on acceptable URL formats.
+ * If an array is given, [[UrlManager::createUrl()]] will be called to create the corresponding URL.
+ * The first element of the array should be the route, and the rest of
+ * the name-value pairs are GET parameters used to construct the URL. For example,
+ *
+ * ~~~
+ * ['admin/index', 'ref' => 1]
+ * ~~~
*/
public function setReturnUrl($url)
{
@@ -284,9 +328,10 @@ class User extends Component
$this->setReturnUrl($request->getUrl());
}
if ($this->loginUrl !== null) {
- Yii::$app->getResponse()->redirect($this->loginUrl);
+ Yii::$app->getResponse()->redirect($this->loginUrl)->send();
+ exit();
} else {
- throw new HttpException(403, Yii::t('yii', 'Login Required'));
+ throw new AccessDeniedHttpException(Yii::t('yii', 'Login Required'));
}
}
@@ -295,16 +340,16 @@ class User extends Component
* The default implementation will trigger the [[EVENT_BEFORE_LOGIN]] event.
* If you override this method, make sure you call the parent implementation
* so that the event is triggered.
- * @param Identity $identity the user identity information
+ * @param IdentityInterface $identity the user identity information
* @param boolean $cookieBased whether the login is cookie-based
* @return boolean whether the user should continue to be logged in
*/
protected function beforeLogin($identity, $cookieBased)
{
- $event = new UserEvent(array(
+ $event = new UserEvent([
'identity' => $identity,
'cookieBased' => $cookieBased,
- ));
+ ]);
$this->trigger(self::EVENT_BEFORE_LOGIN, $event);
return $event->isValid;
}
@@ -314,15 +359,15 @@ class User extends Component
* The default implementation will trigger the [[EVENT_AFTER_LOGIN]] event.
* If you override this method, make sure you call the parent implementation
* so that the event is triggered.
- * @param Identity $identity the user identity information
+ * @param IdentityInterface $identity the user identity information
* @param boolean $cookieBased whether the login is cookie-based
*/
protected function afterLogin($identity, $cookieBased)
{
- $this->trigger(self::EVENT_AFTER_LOGIN, new UserEvent(array(
+ $this->trigger(self::EVENT_AFTER_LOGIN, new UserEvent([
'identity' => $identity,
'cookieBased' => $cookieBased,
- )));
+ ]));
}
/**
@@ -330,14 +375,14 @@ class User extends Component
* The default implementation will trigger the [[EVENT_BEFORE_LOGOUT]] event.
* If you override this method, make sure you call the parent implementation
* so that the event is triggered.
- * @param Identity $identity the user identity information
+ * @param IdentityInterface $identity the user identity information
* @return boolean whether the user should continue to be logged out
*/
protected function beforeLogout($identity)
{
- $event = new UserEvent(array(
+ $event = new UserEvent([
'identity' => $identity,
- ));
+ ]);
$this->trigger(self::EVENT_BEFORE_LOGOUT, $event);
return $event->isValid;
}
@@ -347,13 +392,13 @@ class User extends Component
* The default implementation will trigger the [[EVENT_AFTER_LOGOUT]] event.
* If you override this method, make sure you call the parent implementation
* so that the event is triggered.
- * @param Identity $identity the user identity information
+ * @param IdentityInterface $identity the user identity information
*/
protected function afterLogout($identity)
{
- $this->trigger(self::EVENT_AFTER_LOGOUT, new UserEvent(array(
+ $this->trigger(self::EVENT_AFTER_LOGOUT, new UserEvent([
'identity' => $identity,
- )));
+ ]));
}
/**
@@ -379,20 +424,20 @@ class User extends Component
/**
* Sends an identity cookie.
* This method is used when [[enableAutoLogin]] is true.
- * It saves [[id]], [[Identity::getAuthKey()|auth key]], and the duration of cookie-based login
+ * It saves [[id]], [[IdentityInterface::getAuthKey()|auth key]], and the duration of cookie-based login
* information in the cookie.
- * @param Identity $identity
+ * @param IdentityInterface $identity
* @param integer $duration number of seconds that the user can remain in logged-in status.
- * @see loginByCookie
+ * @see loginByCookie()
*/
protected function sendIdentityCookie($identity, $duration)
{
$cookie = new Cookie($this->identityCookie);
- $cookie->value = json_encode(array(
+ $cookie->value = json_encode([
$identity->getId(),
$identity->getAuthKey(),
$duration,
- ));
+ ]);
$cookie->expire = time() + $duration;
Yii::$app->getResponse()->getCookies()->add($cookie);
}
@@ -407,7 +452,7 @@ class User extends Component
* This method is mainly called by [[login()]], [[logout()]] and [[loginByCookie()]]
* when the current user needs to be associated with the corresponding identity information.
*
- * @param Identity $identity the identity information to be associated with the current user.
+ * @param IdentityInterface $identity the identity information to be associated with the current user.
* If null, it means switching to be a guest.
* @param integer $duration number of seconds that the user can remain in logged-in status.
* This parameter is used only when `$identity` is not null.
@@ -415,11 +460,13 @@ class User extends Component
public function switchIdentity($identity, $duration = 0)
{
$session = Yii::$app->getSession();
- $session->regenerateID(true);
+ if (!YII_ENV_TEST) {
+ $session->regenerateID(true);
+ }
$this->setIdentity($identity);
$session->remove($this->idVar);
$session->remove($this->authTimeoutVar);
- if ($identity instanceof Identity) {
+ if ($identity instanceof IdentityInterface) {
$session->set($this->idVar, $identity->getId());
if ($this->authTimeout !== null) {
$session->set($this->authTimeoutVar, time() + $this->authTimeout);
@@ -462,10 +509,10 @@ class User extends Component
* before, its result will be directly returned when calling this method to check the same
* operation. If this parameter is false, this method will always call
* [[AuthManager::checkAccess()]] to obtain the up-to-date access result. Note that this
- * caching is effective only within the same request and only works when `$params = array()`.
+ * caching is effective only within the same request and only works when `$params = []`.
* @return boolean whether the operations can be performed by this user.
*/
- public function checkAccess($operation, $params = array(), $allowCaching = true)
+ public function checkAccess($operation, $params = [], $allowCaching = true)
{
$auth = Yii::$app->getAuthManager();
if ($auth === null) {
diff --git a/framework/yii/web/UserEvent.php b/framework/yii/web/UserEvent.php
index 4e39380..8577ef5 100644
--- a/framework/yii/web/UserEvent.php
+++ b/framework/yii/web/UserEvent.php
@@ -10,6 +10,7 @@ namespace yii\web;
use yii\base\Event;
/**
+ * This event class is used for Events triggered by the [[User]] class.
*
* @author Qiang Xue
* @since 2.0
@@ -17,7 +18,7 @@ use yii\base\Event;
class UserEvent extends Event
{
/**
- * @var Identity the identity object associated with this event
+ * @var IdentityInterface the identity object associated with this event
*/
public $identity;
/**
diff --git a/framework/yii/web/VerbFilter.php b/framework/yii/web/VerbFilter.php
index 2b7567f..65d182b 100644
--- a/framework/yii/web/VerbFilter.php
+++ b/framework/yii/web/VerbFilter.php
@@ -10,7 +10,6 @@ namespace yii\web;
use Yii;
use yii\base\ActionEvent;
use yii\base\Behavior;
-use yii\base\HttpException;
/**
* VerbFilter is an action filter that filters by HTTP request methods.
@@ -25,18 +24,18 @@ use yii\base\HttpException;
* ~~~
* public function behaviors()
* {
- * return array(
- * 'verbs' => array(
+ * return [
+ * 'verbs' => [
* 'class' => \yii\web\VerbFilter::className(),
- * 'actions' => array(
- * 'index' => array('get'),
- * 'view' => array('get'),
- * 'create' => array('get', 'post'),
- * 'update' => array('get', 'put', 'post'),
- * 'delete' => array('post', 'delete'),
- * ),
- * ),
- * );
+ * 'actions' => [
+ * 'index' => ['get'],
+ * 'view' => ['get'],
+ * 'create' => ['get', 'post'],
+ * 'update' => ['get', 'put', 'post'],
+ * 'delete' => ['post', 'delete'],
+ * ],
+ * ],
+ * ];
* }
* ~~~
*
@@ -52,8 +51,22 @@ class VerbFilter extends Behavior
* you add an entry with the action id as array key and an array of
* allowed methods (e.g. GET, HEAD, PUT) as the value.
* If an action is not listed all request methods are considered allowed.
+ *
+ * You can use '*' to stand for all actions. When an action is explicitly
+ * specified, it takes precedence over the specification given by '*'.
+ *
+ * For example,
+ *
+ * ~~~
+ * [
+ * 'create' => ['get', 'post'],
+ * 'update' => ['get', 'put', 'post'],
+ * 'delete' => ['post', 'delete'],
+ * '*' => ['get'],
+ * ]
+ * ~~~
*/
- public $actions = array();
+ public $actions = [];
/**
@@ -62,29 +75,36 @@ class VerbFilter extends Behavior
*/
public function events()
{
- return array(
- Controller::EVENT_BEFORE_ACTION => 'beforeAction',
- );
+ return [Controller::EVENT_BEFORE_ACTION => 'beforeAction'];
}
/**
* @param ActionEvent $event
* @return boolean
- * @throws \yii\base\HttpException when the request method is not allowed.
+ * @throws HttpException when the request method is not allowed.
*/
public function beforeAction($event)
{
$action = $event->action->id;
if (isset($this->actions[$action])) {
- $verb = Yii::$app->getRequest()->getMethod();
- $allowed = array_map('strtoupper', $this->actions[$action]);
- if (!in_array($verb, $allowed)) {
- $event->isValid = false;
- // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7
- Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $allowed));
- throw new HttpException(405, 'Method Not Allowed. This url can only handle the following request methods: ' . implode(', ', $allowed));
- }
+ $verbs = $this->actions[$action];
+ } elseif (isset($this->actions['*'])) {
+ $verbs = $this->actions['*'];
+ } else {
+ return $event->isValid;
}
+
+ $verb = Yii::$app->getRequest()->getMethod();
+ $allowed = array_map('strtoupper', $verbs);
+ if (!in_array($verb, array_map('strtoupper', $verbs))) {
+ $event->isValid = false;
+ // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7
+ Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $allowed));
+ throw new MethodNotAllowedHttpException(Yii::t('yii', 'Method Not Allowed. This url can only handle the following request methods: {methods}.', [
+ 'methods' => implode(', ', $allowed),
+ ]));
+ }
+
return $event->isValid;
}
}
diff --git a/framework/yii/web/View.php b/framework/yii/web/View.php
new file mode 100644
index 0000000..790e4fd
--- /dev/null
+++ b/framework/yii/web/View.php
@@ -0,0 +1,449 @@
+view`.
+ *
+ * You can modify its configuration by adding an array to your application config under `components`
+ * as it is shown in the following example:
+ *
+ * ~~~
+ * 'view' => [
+ * 'theme' => 'app\themes\MyTheme',
+ * 'renderers' => [
+ * // you may add Smarty or Twig renderer here
+ * ]
+ * // ...
+ * ]
+ * ~~~
+ *
+ * @property \yii\web\AssetManager $assetManager The asset manager. Defaults to the "assetManager" application
+ * component.
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class View extends \yii\base\View
+{
+ const EVENT_BEGIN_BODY = 'beginBody';
+ /**
+ * @event Event an event that is triggered by [[endBody()]].
+ */
+ const EVENT_END_BODY = 'endBody';
+
+ /**
+ * The location of registered JavaScript code block or files.
+ * This means the location is in the head section.
+ */
+ const POS_HEAD = 1;
+ /**
+ * The location of registered JavaScript code block or files.
+ * This means the location is at the beginning of the body section.
+ */
+ const POS_BEGIN = 2;
+ /**
+ * The location of registered JavaScript code block or files.
+ * This means the location is at the end of the body section.
+ */
+ const POS_END = 3;
+ /**
+ * The location of registered JavaScript code block.
+ * This means the JavaScript code block will be enclosed within `jQuery(document).ready()`.
+ */
+ const POS_READY = 4;
+ /**
+ * This is internally used as the placeholder for receiving the content registered for the head section.
+ */
+ const PH_HEAD = '';
+ /**
+ * This is internally used as the placeholder for receiving the content registered for the beginning of the body section.
+ */
+ const PH_BODY_BEGIN = '';
+ /**
+ * This is internally used as the placeholder for receiving the content registered for the end of the body section.
+ */
+ const PH_BODY_END = '';
+
+ /**
+ * @var AssetBundle[] list of the registered asset bundles. The keys are the bundle names, and the values
+ * are the registered [[AssetBundle]] objects.
+ * @see registerAssetBundle()
+ */
+ public $assetBundles = [];
+ /**
+ * @var string the page title
+ */
+ public $title;
+ /**
+ * @var array the registered meta tags.
+ * @see registerMetaTag()
+ */
+ public $metaTags;
+ /**
+ * @var array the registered link tags.
+ * @see registerLinkTag()
+ */
+ public $linkTags;
+ /**
+ * @var array the registered CSS code blocks.
+ * @see registerCss()
+ */
+ public $css;
+ /**
+ * @var array the registered CSS files.
+ * @see registerCssFile()
+ */
+ public $cssFiles;
+ /**
+ * @var array the registered JS code blocks
+ * @see registerJs()
+ */
+ public $js;
+ /**
+ * @var array the registered JS files.
+ * @see registerJsFile()
+ */
+ public $jsFiles;
+
+ private $_assetManager;
+
+ /**
+ * Registers the asset manager being used by this view object.
+ * @return \yii\web\AssetManager the asset manager. Defaults to the "assetManager" application component.
+ */
+ public function getAssetManager()
+ {
+ return $this->_assetManager ?: Yii::$app->getAssetManager();
+ }
+
+ /**
+ * Sets the asset manager.
+ * @param \yii\web\AssetManager $value the asset manager
+ */
+ public function setAssetManager($value)
+ {
+ $this->_assetManager = $value;
+ }
+
+ /**
+ * Marks the ending of an HTML page.
+ */
+ public function endPage()
+ {
+ $this->trigger(self::EVENT_END_PAGE);
+
+ $content = ob_get_clean();
+ foreach (array_keys($this->assetBundles) as $bundle) {
+ $this->registerAssetFiles($bundle);
+ }
+ echo strtr($content, [
+ self::PH_HEAD => $this->renderHeadHtml(),
+ self::PH_BODY_BEGIN => $this->renderBodyBeginHtml(),
+ self::PH_BODY_END => $this->renderBodyEndHtml(),
+ ]);
+
+ unset(
+ $this->metaTags,
+ $this->linkTags,
+ $this->css,
+ $this->cssFiles,
+ $this->js,
+ $this->jsFiles
+ );
+ }
+
+ /**
+ * Registers all files provided by an asset bundle including depending bundles files.
+ * Removes a bundle from [[assetBundles]] once files are registered.
+ * @param string $name name of the bundle to register
+ */
+ private function registerAssetFiles($name)
+ {
+ if (!isset($this->assetBundles[$name])) {
+ return;
+ }
+ $bundle = $this->assetBundles[$name];
+ if ($bundle) {
+ foreach ($bundle->depends as $dep) {
+ $this->registerAssetFiles($dep);
+ }
+ $bundle->registerAssetFiles($this);
+ }
+ unset($this->assetBundles[$name]);
+ }
+
+ /**
+ * Marks the beginning of an HTML body section.
+ */
+ public function beginBody()
+ {
+ echo self::PH_BODY_BEGIN;
+ $this->trigger(self::EVENT_BEGIN_BODY);
+ }
+
+ /**
+ * Marks the ending of an HTML body section.
+ */
+ public function endBody()
+ {
+ $this->trigger(self::EVENT_END_BODY);
+ echo self::PH_BODY_END;
+ }
+
+ /**
+ * Marks the position of an HTML head section.
+ */
+ public function head()
+ {
+ echo self::PH_HEAD;
+ }
+
+ /**
+ * Registers the named asset bundle.
+ * All dependent asset bundles will be registered.
+ * @param string $name the name of the asset bundle.
+ * @param integer|null $position if set, this forces a minimum position for javascript files.
+ * This will adjust depending assets javascript file position or fail if requirement can not be met.
+ * If this is null, asset bundles position settings will not be changed.
+ * See [[registerJsFile]] for more details on javascript position.
+ * @return AssetBundle the registered asset bundle instance
+ * @throws InvalidConfigException if the asset bundle does not exist or a circular dependency is detected
+ */
+ public function registerAssetBundle($name, $position = null)
+ {
+ if (!isset($this->assetBundles[$name])) {
+ $am = $this->getAssetManager();
+ $bundle = $am->getBundle($name);
+ $this->assetBundles[$name] = false;
+ // register dependencies
+ $pos = isset($bundle->jsOptions['position']) ? $bundle->jsOptions['position'] : null;
+ foreach ($bundle->depends as $dep) {
+ $this->registerAssetBundle($dep, $pos);
+ }
+ $this->assetBundles[$name] = $bundle;
+ } elseif ($this->assetBundles[$name] === false) {
+ throw new InvalidConfigException("A circular dependency is detected for bundle '$name'.");
+ } else {
+ $bundle = $this->assetBundles[$name];
+ }
+
+ if ($position !== null) {
+ $pos = isset($bundle->jsOptions['position']) ? $bundle->jsOptions['position'] : null;
+ if ($pos === null) {
+ $bundle->jsOptions['position'] = $pos = $position;
+ } elseif ($pos > $position) {
+ throw new InvalidConfigException("An asset bundle that depends on '$name' has a higher javascript file position configured than '$name'.");
+ }
+ // update position for all dependencies
+ foreach ($bundle->depends as $dep) {
+ $this->registerAssetBundle($dep, $pos);
+ }
+ }
+ return $bundle;
+ }
+
+ /**
+ * Registers a meta tag.
+ * @param array $options the HTML attributes for the meta tag.
+ * @param string $key the key that identifies the meta tag. If two meta tags are registered
+ * with the same key, the latter will overwrite the former. If this is null, the new meta tag
+ * will be appended to the existing ones.
+ */
+ public function registerMetaTag($options, $key = null)
+ {
+ if ($key === null) {
+ $this->metaTags[] = Html::tag('meta', '', $options);
+ } else {
+ $this->metaTags[$key] = Html::tag('meta', '', $options);
+ }
+ }
+
+ /**
+ * Registers a link tag.
+ * @param array $options the HTML attributes for the link tag.
+ * @param string $key the key that identifies the link tag. If two link tags are registered
+ * with the same key, the latter will overwrite the former. If this is null, the new link tag
+ * will be appended to the existing ones.
+ */
+ public function registerLinkTag($options, $key = null)
+ {
+ if ($key === null) {
+ $this->linkTags[] = Html::tag('link', '', $options);
+ } else {
+ $this->linkTags[$key] = Html::tag('link', '', $options);
+ }
+ }
+
+ /**
+ * Registers a CSS code block.
+ * @param string $css the CSS code block to be registered
+ * @param array $options the HTML attributes for the style tag.
+ * @param string $key the key that identifies the CSS code block. If null, it will use
+ * $css as the key. If two CSS code blocks are registered with the same key, the latter
+ * will overwrite the former.
+ */
+ public function registerCss($css, $options = [], $key = null)
+ {
+ $key = $key ?: md5($css);
+ $this->css[$key] = Html::style($css, $options);
+ }
+
+ /**
+ * Registers a CSS file.
+ * @param string $url the CSS file to be registered.
+ * @param array $options the HTML attributes for the link tag.
+ * @param string $key the key that identifies the CSS script file. If null, it will use
+ * $url as the key. If two CSS files are registered with the same key, the latter
+ * will overwrite the former.
+ */
+ public function registerCssFile($url, $options = [], $key = null)
+ {
+ $key = $key ?: $url;
+ $this->cssFiles[$key] = Html::cssFile($url, $options);
+ }
+
+ /**
+ * Registers a JS code block.
+ * @param string $js the JS code block to be registered
+ * @param integer $position the position at which the JS script tag should be inserted
+ * in a page. The possible values are:
+ *
+ * - [[POS_HEAD]]: in the head section
+ * - [[POS_BEGIN]]: at the beginning of the body section
+ * - [[POS_END]]: at the end of the body section
+ * - [[POS_READY]]: enclosed within jQuery(document).ready(). This is the default value.
+ * Note that by using this position, the method will automatically register the jQuery js file.
+ *
+ * @param string $key the key that identifies the JS code block. If null, it will use
+ * $js as the key. If two JS code blocks are registered with the same key, the latter
+ * will overwrite the former.
+ */
+ public function registerJs($js, $position = self::POS_READY, $key = null)
+ {
+ $key = $key ?: md5($js);
+ $this->js[$position][$key] = $js;
+ if ($position === self::POS_READY) {
+ JqueryAsset::register($this);
+ }
+ }
+
+ /**
+ * Registers a JS file.
+ * Please note that when this file depends on other JS files to be registered before,
+ * for example jQuery, you should use [[registerAssetBundle]] instead.
+ * @param string $url the JS file to be registered.
+ * @param array $options the HTML attributes for the script tag. A special option
+ * named "position" is supported which specifies where the JS script tag should be inserted
+ * in a page. The possible values of "position" are:
+ *
+ * - [[POS_HEAD]]: in the head section
+ * - [[POS_BEGIN]]: at the beginning of the body section
+ * - [[POS_END]]: at the end of the body section. This is the default value.
+ *
+ * @param string $key the key that identifies the JS script file. If null, it will use
+ * $url as the key. If two JS files are registered with the same key, the latter
+ * will overwrite the former.
+ */
+ public function registerJsFile($url, $options = [], $key = null)
+ {
+ $position = isset($options['position']) ? $options['position'] : self::POS_END;
+ unset($options['position']);
+ $key = $key ?: $url;
+ $this->jsFiles[$position][$key] = Html::jsFile($url, $options);
+ }
+
+ /**
+ * Renders the content to be inserted in the head section.
+ * The content is rendered using the registered meta tags, link tags, CSS/JS code blocks and files.
+ * @return string the rendered content
+ */
+ protected function renderHeadHtml()
+ {
+ $lines = [];
+ if (!empty($this->metaTags)) {
+ $lines[] = implode("\n", $this->metaTags);
+ }
+
+ $request = Yii::$app->getRequest();
+ if ($request instanceof \yii\web\Request && $request->enableCsrfValidation) {
+ $lines[] = Html::tag('meta', '', ['name' => 'csrf-var', 'content' => $request->csrfVar]);
+ $lines[] = Html::tag('meta', '', ['name' => 'csrf-token', 'content' => $request->getCsrfToken()]);
+ }
+
+ if (!empty($this->linkTags)) {
+ $lines[] = implode("\n", $this->linkTags);
+ }
+ if (!empty($this->cssFiles)) {
+ $lines[] = implode("\n", $this->cssFiles);
+ }
+ if (!empty($this->css)) {
+ $lines[] = implode("\n", $this->css);
+ }
+ if (!empty($this->jsFiles[self::POS_HEAD])) {
+ $lines[] = implode("\n", $this->jsFiles[self::POS_HEAD]);
+ }
+ if (!empty($this->js[self::POS_HEAD])) {
+ $lines[] = Html::script(implode("\n", $this->js[self::POS_HEAD]), ['type' => 'text/javascript']);
+ }
+ return empty($lines) ? '' : implode("\n", $lines);
+ }
+
+ /**
+ * Renders the content to be inserted at the beginning of the body section.
+ * The content is rendered using the registered JS code blocks and files.
+ * @return string the rendered content
+ */
+ protected function renderBodyBeginHtml()
+ {
+ $lines = [];
+ if (!empty($this->jsFiles[self::POS_BEGIN])) {
+ $lines[] = implode("\n", $this->jsFiles[self::POS_BEGIN]);
+ }
+ if (!empty($this->js[self::POS_BEGIN])) {
+ $lines[] = Html::script(implode("\n", $this->js[self::POS_BEGIN]), ['type' => 'text/javascript']);
+ }
+ return empty($lines) ? '' : implode("\n", $lines);
+ }
+
+ /**
+ * Renders the content to be inserted at the end of the body section.
+ * The content is rendered using the registered JS code blocks and files.
+ * @return string the rendered content
+ */
+ protected function renderBodyEndHtml()
+ {
+ $lines = [];
+ if (!empty($this->jsFiles[self::POS_END])) {
+ $lines[] = implode("\n", $this->jsFiles[self::POS_END]);
+ }
+ if (!empty($this->js[self::POS_END])) {
+ $lines[] = Html::script(implode("\n", $this->js[self::POS_END]), ['type' => 'text/javascript']);
+ }
+ if (!empty($this->js[self::POS_READY])) {
+ $js = "jQuery(document).ready(function(){\n" . implode("\n", $this->js[self::POS_READY]) . "\n});";
+ $lines[] = Html::script($js, ['type' => 'text/javascript']);
+ }
+ return empty($lines) ? '' : implode("\n", $lines);
+ }
+}
diff --git a/framework/yii/web/XmlResponseFormatter.php b/framework/yii/web/XmlResponseFormatter.php
new file mode 100644
index 0000000..292424a
--- /dev/null
+++ b/framework/yii/web/XmlResponseFormatter.php
@@ -0,0 +1,98 @@
+
+ * @since 2.0
+ */
+class XmlResponseFormatter extends Component implements ResponseFormatterInterface
+{
+ /**
+ * @var string the Content-Type header for the response
+ */
+ public $contentType = 'application/xml';
+ /**
+ * @var string the XML version
+ */
+ public $version = '1.0';
+ /**
+ * @var string the XML encoding. If not set, it will use the value of [[Response::charset]].
+ */
+ public $encoding;
+ /**
+ * @var string the name of the root element.
+ */
+ public $rootTag = 'response';
+ /**
+ * @var string the name of the elements that represent the array elements with numeric keys.
+ */
+ public $itemTag = 'item';
+
+ /**
+ * Formats the specified response.
+ * @param Response $response the response to be formatted.
+ */
+ public function format($response)
+ {
+ $response->getHeaders()->set('Content-Type', $this->contentType);
+ $dom = new DOMDocument($this->version, $this->encoding === null ? $response->charset : $this->encoding);
+ $root = new DOMElement($this->rootTag);
+ $dom->appendChild($root);
+ $this->buildXml($root, $response->data);
+ $response->content = $dom->saveXML();
+ }
+
+ /**
+ * @param DOMElement $element
+ * @param mixed $data
+ */
+ protected function buildXml($element, $data)
+ {
+ if (is_object($data)) {
+ $child = new DOMElement(StringHelper::basename(get_class($data)));
+ $element->appendChild($child);
+ if ($data instanceof Arrayable) {
+ $this->buildXml($child, $data->toArray());
+ } else {
+ $array = [];
+ foreach ($data as $name => $value) {
+ $array[$name] = $value;
+ }
+ $this->buildXml($child, $array);
+ }
+ } elseif (is_array($data)) {
+ foreach ($data as $name => $value) {
+ if (is_int($name) && is_object($value)) {
+ $this->buildXml($element, $value);
+ } elseif (is_array($value) || is_object($value)) {
+ $child = new DOMElement(is_int($name) ? $this->itemTag : $name);
+ $element->appendChild($child);
+ $this->buildXml($child, $value);
+ } else {
+ $child = new DOMElement(is_int($name) ? $this->itemTag : $name);
+ $element->appendChild($child);
+ $child->appendChild(new DOMText((string)$value));
+ }
+ }
+ } else {
+ $element->appendChild(new DOMText((string)$data));
+ }
+ }
+}
diff --git a/framework/yii/web/YiiAsset.php b/framework/yii/web/YiiAsset.php
new file mode 100644
index 0000000..d38b711
--- /dev/null
+++ b/framework/yii/web/YiiAsset.php
@@ -0,0 +1,25 @@
+
+ * @since 2.0
+ */
+class YiiAsset extends AssetBundle
+{
+ public $sourcePath = '@yii/assets';
+ public $js = [
+ 'yii.js',
+ ];
+ public $depends = [
+ 'yii\web\JqueryAsset',
+ ];
+}
diff --git a/framework/yii/widgets/ActiveField.php b/framework/yii/widgets/ActiveField.php
index 346f743..bd26237 100644
--- a/framework/yii/widgets/ActiveField.php
+++ b/framework/yii/widgets/ActiveField.php
@@ -8,7 +8,7 @@ namespace yii\widgets;
use Yii;
use yii\base\Component;
-use yii\db\ActiveRecord;
+use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\base\Model;
use yii\web\JsExpression;
@@ -32,37 +32,45 @@ class ActiveField extends Component
*/
public $attribute;
/**
- * @var string the tag name for the field container.
- */
- public $tag = 'div';
- /**
* @var array the HTML attributes (name-value pairs) for the field container tag.
* The values will be HTML-encoded using [[Html::encode()]].
* If a value is null, the corresponding attribute will not be rendered.
+ * The following special options are recognized:
+ *
+ * - tag: the tag name of the container element. Defaults to "div".
*/
- public $options = array(
- 'class' => 'control-group',
- );
+ public $options = ['class' => 'form-group'];
/**
- * @var string the template that is used to arrange the label, the input and the error message.
- * The following tokens will be replaced when [[render()]] is called: `{label}`, `{input}` and `{error}`.
+ * @var string the template that is used to arrange the label, the input field, the error message and the hint text.
+ * The following tokens will be replaced when [[render()]] is called: `{label}`, `{input}`, `{error}` and `{hint}`.
*/
- public $template = "{label}\n\n{input}\n{error}\n
";
+ public $template = "{label}\n{input}\n{error}\n{hint}";
/**
* @var array the default options for the input tags. The parameter passed to individual input methods
* (e.g. [[textInput()]]) will be merged with this property when rendering the input tag.
*/
- public $inputOptions = array();
+ public $inputOptions = ['class' => 'form-control'];
/**
* @var array the default options for the error tags. The parameter passed to [[error()]] will be
* merged with this property when rendering the error tag.
+ * The following special options are recognized:
+ *
+ * - tag: the tag name of the container element. Defaults to "div".
*/
- public $errorOptions = array('tag' => 'span', 'class' => 'help-inline');
+ public $errorOptions = ['class' => 'help-block'];
/**
* @var array the default options for the label tags. The parameter passed to [[label()]] will be
* merged with this property when rendering the label tag.
*/
- public $labelOptions = array('class' => 'control-label');
+ public $labelOptions = ['class' => 'control-label'];
+ /**
+ * @var array the default options for the hint tags. The parameter passed to [[hint()]] will be
+ * merged with this property when rendering the hint tag.
+ * The following special options are recognized:
+ *
+ * - tag: the tag name of the container element. Defaults to "div".
+ */
+ public $hintOptions = ['class' => 'hint-block'];
/**
* @var boolean whether to enable client-side data validation.
* If not set, it will take the value of [[ActiveForm::enableClientValidation]].
@@ -93,7 +101,7 @@ class ActiveField extends Component
/**
* @var array the jQuery selectors for selecting the container, input and error tags.
* The array keys should be "container", "input", and/or "error", and the array values
- * are the corresponding selectors. For example, `array('input' => '#my-input')`.
+ * are the corresponding selectors. For example, `['input' => '#my-input']`.
*
* The container selector is used under the context of the form, while the input and the error
* selectors are used under the context of the container.
@@ -101,7 +109,70 @@ class ActiveField extends Component
* You normally do not need to set this property as the default selectors should work well for most cases.
*/
public $selectors;
+ /**
+ * @var array different parts of the field (e.g. input, label). This will be used together with
+ * [[template]] to generate the final field HTML code. The keys are the token names in [[template]],
+ * while the values are the corresponding HTML code. Valid tokens include `{input}`, `{label}` and `{error}`.
+ * Note that you normally don't need to access this property directly as
+ * it is maintained by various methods of this class.
+ */
+ public $parts = [];
+
+ /**
+ * PHP magic method that returns the string representation of this object.
+ * @return string the string representation of this object.
+ */
+ public function __toString()
+ {
+ // __toString cannot throw exception
+ // use trigger_error to bypass this limitation
+ try {
+ return $this->render();
+ } catch (\Exception $e) {
+ trigger_error($e->getMessage());
+ return '';
+ }
+ }
+
+ /**
+ * Renders the whole field.
+ * This method will generate the label, error tag, input tag and hint tag (if any), and
+ * assemble them into HTML according to [[template]].
+ * @param string|callable $content the content within the field container.
+ * If null (not set), the default methods will be called to generate the label, error tag and input tag,
+ * and use them as the content.
+ * If a callable, it will be called to generate the content. The signature of the callable should be:
+ *
+ * ~~~
+ * function ($field) {
+ * return $html;
+ * }
+ * ~~~
+ *
+ * @return string the rendering result
+ */
+ public function render($content = null)
+ {
+ if ($content === null) {
+ if (!isset($this->parts['{input}'])) {
+ $this->parts['{input}'] = Html::activeTextInput($this->model, $this->attribute, $this->inputOptions);
+ }
+ if (!isset($this->parts['{label}'])) {
+ $this->parts['{label}'] = Html::activeLabel($this->model, $this->attribute, $this->labelOptions);
+ }
+ if (!isset($this->parts['{error}'])) {
+ $this->parts['{error}'] = Html::error($this->model, $this->attribute, $this->errorOptions);
+ }
+ if (!isset($this->parts['{hint}'])) {
+ $this->parts['{hint}'] = '';
+ }
+ $content = strtr($this->template, $this->parts);
+ } elseif (!is_string($content)) {
+ $content = call_user_func($content, $this);
+ }
+ return $this->begin() . "\n" . $content . "\n" . $this->end();
+ }
/**
* Renders the opening tag of the field container.
@@ -109,15 +180,15 @@ class ActiveField extends Component
*/
public function begin()
{
- $options = $this->getClientOptions();
- if (!empty($options)) {
- $this->form->attributes[$this->attribute] = $options;
+ $clientOptions = $this->getClientOptions();
+ if (!empty($clientOptions)) {
+ $this->form->attributes[$this->attribute] = $clientOptions;
}
$inputID = Html::getInputId($this->model, $this->attribute);
$attribute = Html::getAttributeName($this->attribute);
$options = $this->options;
- $class = isset($options['class']) ? array($options['class']) : array();
+ $class = isset($options['class']) ? [$options['class']] : [];
$class[] = "field-$inputID";
if ($this->model->isAttributeRequired($attribute)) {
$class[] = $this->form->requiredCssClass;
@@ -126,8 +197,9 @@ class ActiveField extends Component
$class[] = $this->form->errorCssClass;
}
$options['class'] = implode(' ', $class);
+ $tag = ArrayHelper::remove($options, 'tag', 'div');
- return Html::beginTag($this->tag, $options);
+ return Html::beginTag($tag, $options);
}
/**
@@ -136,79 +208,26 @@ class ActiveField extends Component
*/
public function end()
{
- return Html::endTag($this->tag);
- }
-
- /**
- * Returns the JS options for the field.
- * @return array the JS options
- */
- protected function getClientOptions()
- {
- $enableClientValidation = $this->enableClientValidation || $this->enableClientValidation === null && $this->form->enableClientValidation;
- $enableAjaxValidation = $this->enableAjaxValidation || $this->enableAjaxValidation === null && $this->form->enableAjaxValidation;
- if ($enableClientValidation) {
- $attribute = Html::getAttributeName($this->attribute);
- $validators = array();
- foreach ($this->model->getActiveValidators($attribute) as $validator) {
- /** @var \yii\validators\Validator $validator */
- $js = $validator->clientValidateAttribute($this->model, $attribute, $this->form->getView());
- if ($validator->enableClientValidation && $js != '') {
- $validators[] = $js;
- }
- }
- if (!empty($validators)) {
- $options['validate'] = new JsExpression("function(attribute, value, messages) {" . implode('', $validators) . '}');
- }
- }
-
- if ($enableAjaxValidation) {
- $options['enableAjaxValidation'] = 1;
- }
-
- if ($enableClientValidation && !empty($options['validate']) || $enableAjaxValidation) {
- $inputID = Html::getInputId($this->model, $this->attribute);
- $options['name'] = $inputID;
- $names = array(
- 'validateOnChange',
- 'validateOnType',
- 'validationDelay',
- );
- foreach ($names as $name) {
- $options[$name] = $this->$name === null ? $this->form->$name : $this->$name;
- }
- $options['container'] = isset($this->selectors['container']) ? $this->selectors['container'] : ".field-$inputID";
- $options['input'] = isset($this->selectors['input']) ? $this->selectors['input'] : "#$inputID";
- if (isset($this->errorOptions['class'])) {
- $options['error'] = '.' . implode('.', preg_split('/\s+/', $this->errorOptions['class'], -1, PREG_SPLIT_NO_EMPTY));
- } else {
- $options['error'] = isset($this->errorOptions['tag']) ? $this->errorOptions['tag'] : 'span';
- }
- return $options;
- } else {
- return array();
- }
+ return Html::endTag(isset($this->options['tag']) ? $this->options['tag'] : 'div');
}
/**
* Generates a label tag for [[attribute]].
- * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]].
+ * @param string $label the label to use. If null, it will be generated via [[Model::getAttributeLabel()]].
+ * Note that this will NOT be [[Html::encode()|encoded]].
* @param array $options the tag options in terms of name-value pairs. It will be merged with [[labelOptions]].
* The options will be rendered as the attributes of the resulting tag. The values will be HTML-encoded
- * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
- *
- * The following options are specially handled:
- *
- * - label: this specifies the label to be displayed. Note that this will NOT be [[encoded()]].
- * If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display
- * (after encoding).
- *
- * @return string the generated label tag
+ * using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ * @return static the field object itself
*/
- public function label($options = array())
+ public function label($label = null, $options = [])
{
$options = array_merge($this->labelOptions, $options);
- return Html::activeLabel($this->model, $this->attribute, $options);
+ if ($label !== null) {
+ $options['label'] = $label;
+ }
+ $this->parts['{label}'] = Html::activeLabel($this->model, $this->attribute, $options);
+ return $this;
}
/**
@@ -216,125 +235,119 @@ class ActiveField extends Component
* Note that even if there is no validation error, this method will still return an empty error tag.
* @param array $options the tag options in terms of name-value pairs. It will be merged with [[errorOptions]].
* The options will be rendered as the attributes of the resulting tag. The values will be HTML-encoded
- * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ * using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
*
* The following options are specially handled:
*
- * - tag: this specifies the tag name. If not set, "span" will be used.
+ * - tag: this specifies the tag name. If not set, "div" will be used.
*
- * @return string the generated label tag
+ * @return static the field object itself
*/
- public function error($options = array())
+ public function error($options = [])
{
$options = array_merge($this->errorOptions, $options);
- $attribute = Html::getAttributeName($this->attribute);
- $error = $this->model->getFirstError($attribute);
- $tag = isset($options['tag']) ? $options['tag'] : 'span';
- unset($options['tag']);
- return Html::tag($tag, Html::encode($error), $options);
+ $this->parts['{error}'] = Html::error($this->model, $this->attribute, $options);
+ return $this;
}
/**
- * Renders the field with the given input HTML.
- * This method will generate the label and error tags, and return them together with the given
- * input HTML according to [[template]].
- * @param string $input the input HTML
- * @return string the rendering result
- */
- public function render($input)
- {
- return $this->begin() . "\n" . strtr($this->template, array(
- '{input}' => $input,
- '{label}' => $this->label(),
- '{error}' => $this->error(),
- )) . "\n" . $this->end();
- }
-
- /**
- * Renders a field containing an input field.
- * @param string $type the input type (e.g. 'text', 'password')
+ * Renders the hint tag.
+ * @param string $content the hint content. It will NOT be HTML-encoded.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the generated input tag
+ * the attributes of the hint tag. The values will be HTML-encoded using [[Html::encode()]].
+ *
+ * The following options are specially handled:
+ *
+ * - tag: this specifies the tag name. If not set, "div" will be used.
+ *
+ * @return static the field object itself
*/
- public function input($type, $options = array())
+ public function hint($content, $options = [])
{
- $options = array_merge($this->inputOptions, $options);
- return $this->render(Html::activeInput($type, $this->model, $this->attribute, $options));
+ $options = array_merge($this->hintOptions, $options);
+ $tag = ArrayHelper::remove($options, 'tag', 'div');
+ $this->parts['{hint}'] = Html::tag($tag, $content, $options);
+ return $this;
}
/**
- * Renders a field containing a text input.
- * This method will generate the "name" and "value" tag attributes automatically for the model attribute
- * unless they are explicitly specified in `$options`.
+ * Renders an input tag.
+ * @param string $type the input type (e.g. 'text', 'password')
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the rendering result
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
+ * @return static the field object itself
*/
- public function textInput($options = array())
+ public function input($type, $options = [])
{
$options = array_merge($this->inputOptions, $options);
- return $this->render(Html::activeTextInput($this->model, $this->attribute, $options));
+ $this->parts['{input}'] = Html::activeInput($type, $this->model, $this->attribute, $options);
+ return $this;
}
/**
- * Renders a field containing a hidden input.
+ * Renders a text input.
* This method will generate the "name" and "value" tag attributes automatically for the model attribute
* unless they are explicitly specified in `$options`.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the rendering result
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
+ * @return static the field object itself
*/
- public function hiddenInput($options = array())
+ public function textInput($options = [])
{
$options = array_merge($this->inputOptions, $options);
- return $this->render(Html::activeHiddenInput($this->model, $this->attribute, $options));
+ $this->parts['{input}'] = Html::activeTextInput($this->model, $this->attribute, $options);
+ return $this;
}
/**
- * Renders a field containing a password input.
+ * Renders a password input.
* This method will generate the "name" and "value" tag attributes automatically for the model attribute
* unless they are explicitly specified in `$options`.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the rendering result
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
+ * @return static the field object itself
*/
- public function passwordInput($options = array())
+ public function passwordInput($options = [])
{
$options = array_merge($this->inputOptions, $options);
- return $this->render(Html::activePasswordInput($this->model, $this->attribute, $options));
+ $this->parts['{input}'] = Html::activePasswordInput($this->model, $this->attribute, $options);
+ return $this;
}
/**
- * Renders a field containing a file input.
+ * Renders a file input.
* This method will generate the "name" and "value" tag attributes automatically for the model attribute
* unless they are explicitly specified in `$options`.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the rendering result
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
+ * @return static the field object itself
*/
- public function fileInput($options = array())
+ public function fileInput($options = [])
{
- $options = array_merge($this->inputOptions, $options);
- return $this->render(Html::activeFileInput($this->model, $this->attribute, $options));
+ // https://github.com/yiisoft/yii2/pull/795
+ if ($this->inputOptions !== ['class' => 'form-control']) {
+ $options = array_merge($this->inputOptions, $options);
+ }
+ $this->parts['{input}'] = Html::activeFileInput($this->model, $this->attribute, $options);
+ return $this;
}
/**
- * Renders a field containing a text area.
+ * Renders a text area.
* The model attribute value will be used as the content in the textarea.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
- * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
- * @return string the rendering result
+ * the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
+ * @return static the field object itself
*/
- public function textarea($options = array())
+ public function textarea($options = [])
{
$options = array_merge($this->inputOptions, $options);
- return $this->render(Html::activeTextarea($this->model, $this->attribute, $options));
+ $this->parts['{input}'] = Html::activeTextarea($this->model, $this->attribute, $options);
+ return $this;
}
/**
- * Renders a field containing a radio button.
- * This method will generate the "name" tag attribute automatically unless it is explicitly specified in `$options`.
+ * Renders a radio button.
* This method will generate the "checked" tag attribute according to the model attribute value.
* @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
*
@@ -342,38 +355,35 @@ class ActiveField extends Component
* it will take the default value '0'. This method will render a hidden input so that if the radio button
* is not checked and is submitted, the value of this attribute will still be submitted to the server
* via the hidden input.
+ * - label: string, a label displayed next to the radio button. It will NOT be HTML-encoded. Therefore you can pass
+ * in HTML code such as an image tag. If this is is coming from end users, you should [[Html::encode()]] it to prevent XSS attacks.
+ * When this option is specified, the radio button will be enclosed by a label tag.
+ * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
*
* The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ * be HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
* @param boolean $enclosedByLabel whether to enclose the radio within the label.
* If true, the method will still use [[template]] to layout the checkbox and the error message
* except that the radio is enclosed by the label tag.
- * @return string the rendering result
+ * @return static the field object itself
*/
- public function radio($options = array(), $enclosedByLabel = true)
+ public function radio($options = [], $enclosedByLabel = true)
{
- $options = array_merge($this->inputOptions, $options);
if ($enclosedByLabel) {
- $hidden = '';
- $radio = Html::activeRadio($this->model, $this->attribute, $options);
- if (($pos = strpos($radio, '><')) !== false) {
- $hidden = substr($radio, 0, $pos + 1);
- $radio = substr($radio, $pos + 1);
+ if (!isset($options['label'])) {
+ $attribute = Html::getAttributeName($this->attribute);
+ $options['label'] = Html::encode($this->model->getAttributeLabel($attribute));
}
- $label = isset($this->labelOptions['label']) ? $this->labelOptions['label'] : Html::encode($this->model->getAttributeLabel($this->attribute));
- return $this->begin() . "\n" . $hidden . strtr($this->template, array(
- '{input}' => Html::label("$radio $label", null, array('class' => 'radio')),
- '{label}' => '',
- '{error}' => $this->error(),
- )) . "\n" . $this->end();
+ $this->parts['{input}'] = Html::activeRadio($this->model, $this->attribute, $options);
+ $this->parts['{label}'] = '';
} else {
- return $this->render(Html::activeRadio($this->model, $this->attribute, $options));
+ $this->parts['{input}'] = Html::activeRadio($this->model, $this->attribute, $options);
}
+ return $this;
}
/**
- * Renders a field containing a checkbox.
- * This method will generate the "name" tag attribute automatically unless it is explicitly specified in `$options`.
+ * Renders a checkbox.
* This method will generate the "checked" tag attribute according to the model attribute value.
* @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
*
@@ -381,43 +391,41 @@ class ActiveField extends Component
* it will take the default value '0'. This method will render a hidden input so that if the radio button
* is not checked and is submitted, the value of this attribute will still be submitted to the server
* via the hidden input.
+ * - label: string, a label displayed next to the checkbox. It will NOT be HTML-encoded. Therefore you can pass
+ * in HTML code such as an image tag. If this is is coming from end users, you should [[Html::encode()]] it to prevent XSS attacks.
+ * When this option is specified, the checkbox will be enclosed by a label tag.
+ * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
*
* The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ * be HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
* @param boolean $enclosedByLabel whether to enclose the checkbox within the label.
* If true, the method will still use [[template]] to layout the checkbox and the error message
* except that the checkbox is enclosed by the label tag.
- * @return string the rendering result
+ * @return static the field object itself
*/
- public function checkbox($options = array(), $enclosedByLabel = true)
+ public function checkbox($options = [], $enclosedByLabel = true)
{
- $options = array_merge($this->inputOptions, $options);
if ($enclosedByLabel) {
- $hidden = '';
- $checkbox = Html::activeCheckbox($this->model, $this->attribute, $options);
- if (($pos = strpos($checkbox, '><')) !== false) {
- $hidden = substr($checkbox, 0, $pos + 1);
- $checkbox = substr($checkbox, $pos + 1);
+ if (!isset($options['label'])) {
+ $attribute = Html::getAttributeName($this->attribute);
+ $options['label'] = Html::encode($this->model->getAttributeLabel($attribute));
}
- $label = isset($this->labelOptions['label']) ? $this->labelOptions['label'] : Html::encode($this->model->getAttributeLabel($this->attribute));
- return $this->begin() . "\n" . $hidden . strtr($this->template, array(
- '{input}' => Html::label("$checkbox $label", null, array('class' => 'checkbox')),
- '{label}' => '',
- '{error}' => $this->error(),
- )) . "\n" . $this->end();
+ $this->parts['{input}'] = Html::activeCheckbox($this->model, $this->attribute, $options);
+ $this->parts['{label}'] = '';
} else {
- return $this->render(Html::activeCheckbox($this->model, $this->attribute, $options));
+ $this->parts['{input}'] = Html::activeCheckbox($this->model, $this->attribute, $options);
}
+ return $this;
}
/**
- * Renders a field containing a drop-down list.
+ * Renders a drop-down list.
* The selection of the drop-down list is taken from the value of the model attribute.
* @param array $items the option data items. The array keys are option values, and the array values
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
* If you have a list of data models, you may convert them into the format described above using
- * [[\yii\helpers\ArrayHelper::map()]].
+ * [[ArrayHelper::map()]].
*
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
* the labels will also be HTML-encoded.
@@ -428,28 +436,29 @@ class ActiveField extends Component
* and the array values are the extra attributes for the corresponding option tags. For example,
*
* ~~~
- * array(
- * 'value1' => array('disabled' => true),
- * 'value2' => array('label' => 'value 2'),
- * );
+ * [
+ * 'value1' => ['disabled' => true],
+ * 'value2' => ['label' => 'value 2'],
+ * ];
* ~~~
*
* - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
* except that the array keys represent the optgroup labels specified in $items.
*
* The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ * be HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
*
- * @return string the rendering result
+ * @return static the field object itself
*/
- public function dropDownList($items, $options = array())
+ public function dropDownList($items, $options = [])
{
$options = array_merge($this->inputOptions, $options);
- return $this->render(Html::activeDropDownList($this->model, $this->attribute, $items, $options));
+ $this->parts['{input}'] = Html::activeDropDownList($this->model, $this->attribute, $items, $options);
+ return $this;
}
/**
- * Renders a field containing a list box.
+ * Renders a list box.
* The selection of the list box is taken from the value of the model attribute.
* @param array $items the option data items. The array keys are option values, and the array values
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
@@ -466,10 +475,10 @@ class ActiveField extends Component
* and the array values are the extra attributes for the corresponding option tags. For example,
*
* ~~~
- * array(
- * 'value1' => array('disabled' => true),
- * 'value2' => array('label' => 'value 2'),
- * );
+ * [
+ * 'value1' => ['disabled' => true],
+ * 'value2' => ['label' => 'value 2'],
+ * ];
* ~~~
*
* - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
@@ -479,23 +488,24 @@ class ActiveField extends Component
* mode, we can still obtain the posted unselect value.
*
* The rest of the options will be rendered as the attributes of the resulting tag. The values will
- * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+ * be HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
*
- * @return string the rendering result
+ * @return static the field object itself
*/
- public function listBox($items, $options = array())
+ public function listBox($items, $options = [])
{
$options = array_merge($this->inputOptions, $options);
- return $this->render(Html::activeListBox($this->model, $this->attribute, $items, $options));
+ $this->parts['{input}'] = Html::activeListBox($this->model, $this->attribute, $items, $options);
+ return $this;
}
/**
- * Renders a field containing a list of checkboxes.
+ * Renders a list of checkboxes.
* A checkbox list allows multiple selection, like [[listBox()]].
* As a result, the corresponding submitted value is an array.
* The selection of the checkbox list is taken from the value of the model attribute.
* @param array $items the data item used to generate the checkboxes.
- * The array keys are the labels, while the array values are the corresponding checkbox values.
+ * The array values are the labels, while the array keys are the corresponding checkbox values.
* Note that the labels will NOT be HTML-encoded, while the values will.
* @param array $options options (name => config) for the checkbox list. The following options are specially handled:
*
@@ -512,19 +522,16 @@ class ActiveField extends Component
* where $index is the zero-based index of the checkbox in the whole list; $label
* is the label for the checkbox; and $name, $value and $checked represent the name,
* value and the checked status of the checkbox input.
- * @return string the rendering result
+ * @return static the field object itself
*/
- public function checkboxList($items, $options = array())
+ public function checkboxList($items, $options = [])
{
- return $this->render(
- ''
- . Html::activeCheckboxList($this->model, $this->attribute, $items, $options)
- . '
'
- );
+ $this->parts['{input}'] = Html::activeCheckboxList($this->model, $this->attribute, $items, $options);
+ return $this;
}
/**
- * Renders a field containing a list of radio buttons.
+ * Renders a list of radio buttons.
* A radio button list is like a checkbox list, except that it only allows single selection.
* The selection of the radio buttons is taken from the value of the model attribute.
* @param array $items the data item used to generate the radio buttons.
@@ -545,19 +552,16 @@ class ActiveField extends Component
* where $index is the zero-based index of the radio button in the whole list; $label
* is the label for the radio button; and $name, $value and $checked represent the name,
* value and the checked status of the radio button input.
- * @return string the rendering result
+ * @return static the field object itself
*/
- public function radioList($items, $options = array())
+ public function radioList($items, $options = [])
{
- return $this->render(
- ''
- . Html::activeRadioList($this->model, $this->attribute, $items, $options)
- . '
'
- );
+ $this->parts['{input}'] = Html::activeRadioList($this->model, $this->attribute, $items, $options);
+ return $this;
}
/**
- * Renders a field containing an input widget.
+ * Renders a widget as the input of the field.
*
* Note that the widget must have both `model` and `attribute` properties. They will
* be initialized with [[model]] and [[attribute]] of this field, respectively.
@@ -567,14 +571,65 @@ class ActiveField extends Component
*
* @param string $class the widget class name
* @param array $config name-value pairs that will be used to initialize the widget
- * @return string the rendering result
+ * @return static the field object itself
*/
- public function widget($class, $config = array())
+ public function widget($class, $config = [])
{
/** @var \yii\base\Widget $class */
$config['model'] = $this->model;
$config['attribute'] = $this->attribute;
$config['view'] = $this->form->getView();
- return $this->render($class::widget($config));
+ $this->parts['{input}'] = $class::widget($config);
+ return $this;
+ }
+
+ /**
+ * Returns the JS options for the field.
+ * @return array the JS options
+ */
+ protected function getClientOptions()
+ {
+ $attribute = Html::getAttributeName($this->attribute);
+ if (!in_array($attribute, $this->model->activeAttributes(), true)) {
+ return [];
+ }
+
+ $enableClientValidation = $this->enableClientValidation || $this->enableClientValidation === null && $this->form->enableClientValidation;
+ if ($enableClientValidation) {
+ $validators = [];
+ foreach ($this->model->getActiveValidators($attribute) as $validator) {
+ /** @var \yii\validators\Validator $validator */
+ $js = $validator->clientValidateAttribute($this->model, $attribute, $this->form->getView());
+ if ($validator->enableClientValidation && $js != '') {
+ $validators[] = $js;
+ }
+ }
+ if (!empty($validators)) {
+ $options['validate'] = new JsExpression("function(attribute, value, messages) {" . implode('', $validators) . '}');
+ }
+ }
+
+ $enableAjaxValidation = $this->enableAjaxValidation || $this->enableAjaxValidation === null && $this->form->enableAjaxValidation;
+ if ($enableAjaxValidation) {
+ $options['enableAjaxValidation'] = 1;
+ }
+
+ if ($enableClientValidation && !empty($options['validate']) || $enableAjaxValidation) {
+ $inputID = Html::getInputId($this->model, $this->attribute);
+ $options['name'] = $inputID;
+ foreach (['validateOnChange', 'validateOnType', 'validationDelay'] as $name) {
+ $options[$name] = $this->$name === null ? $this->form->$name : $this->$name;
+ }
+ $options['container'] = isset($this->selectors['container']) ? $this->selectors['container'] : ".field-$inputID";
+ $options['input'] = isset($this->selectors['input']) ? $this->selectors['input'] : "#$inputID";
+ if (isset($this->errorOptions['class'])) {
+ $options['error'] = '.' . implode('.', preg_split('/\s+/', $this->errorOptions['class'], -1, PREG_SPLIT_NO_EMPTY));
+ } else {
+ $options['error'] = isset($this->errorOptions['tag']) ? $this->errorOptions['tag'] : 'div';
+ }
+ return $options;
+ } else {
+ return [];
+ }
}
}
diff --git a/framework/yii/widgets/ActiveForm.php b/framework/yii/widgets/ActiveForm.php
index eb14293..b218a2e 100644
--- a/framework/yii/widgets/ActiveForm.php
+++ b/framework/yii/widgets/ActiveForm.php
@@ -12,6 +12,7 @@ use yii\base\Widget;
use yii\base\Model;
use yii\helpers\Html;
use yii\helpers\Json;
+use yii\web\JsExpression;
/**
* ActiveForm ...
@@ -35,7 +36,7 @@ class ActiveForm extends Widget
* The values will be HTML-encoded using [[Html::encode()]].
* If a value is null, the corresponding attribute will not be rendered.
*/
- public $options = array();
+ public $options = [];
/**
* @var array the default configuration used by [[field()]] when creating a new field object.
*/
@@ -52,11 +53,11 @@ class ActiveForm extends Widget
/**
* @var string the CSS class that is added to a field container when the associated attribute has validation error.
*/
- public $errorCssClass = 'error';
+ public $errorCssClass = 'has-error';
/**
* @var string the CSS class that is added to a field container when the associated attribute is successfully validated.
*/
- public $successCssClass = 'success';
+ public $successCssClass = 'has-success';
/**
* @var string the CSS class that is added to a field container when the associated attribute is being validated.
*/
@@ -103,11 +104,43 @@ class ActiveForm extends Widget
*/
public $ajaxVar = 'ajax';
/**
+ * @var string|JsExpression a JS callback that will be called when the form is being submitted.
+ * The signature of the callback should be:
+ *
+ * ~~~
+ * function ($form) {
+ * ...return false to cancel submission...
+ * }
+ * ~~~
+ */
+ public $beforeSubmit;
+ /**
+ * @var string|JsExpression a JS callback that is called before validating an attribute.
+ * The signature of the callback should be:
+ *
+ * ~~~
+ * function ($form, attribute, messages) {
+ * ...return false to cancel the validation...
+ * }
+ * ~~~
+ */
+ public $beforeValidate;
+ /**
+ * @var string|JsExpression a JS callback that is called after validating an attribute.
+ * The signature of the callback should be:
+ *
+ * ~~~
+ * function ($form, attribute, messages) {
+ * }
+ * ~~~
+ */
+ public $afterValidate;
+ /**
* @var array the client validation options for individual attributes. Each element of the array
* represents the validation options for a particular attribute.
* @internal
*/
- public $attributes = array();
+ public $attributes = [];
/**
* Initializes the widget.
@@ -119,7 +152,7 @@ class ActiveForm extends Widget
$this->options['id'] = $this->getId();
}
if (!isset($this->fieldConfig['class'])) {
- $this->fieldConfig['class'] = 'yii\widgets\ActiveField';
+ $this->fieldConfig['class'] = ActiveField::className();
}
echo Html::beginForm($this->action, $this->method, $this->options);
}
@@ -134,8 +167,9 @@ class ActiveForm extends Widget
$id = $this->options['id'];
$options = Json::encode($this->getClientOptions());
$attributes = Json::encode($this->attributes);
- $this->getView()->registerAssetBundle('yii/form');
- $this->getView()->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);");
+ $view = $this->getView();
+ ActiveFormAsset::register($view);
+ $view->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);");
}
echo Html::endForm();
}
@@ -146,17 +180,22 @@ class ActiveForm extends Widget
*/
protected function getClientOptions()
{
- $options = array(
+ $options = [
'errorSummary' => '.' . $this->errorSummaryCssClass,
'validateOnSubmit' => $this->validateOnSubmit,
'errorCssClass' => $this->errorCssClass,
'successCssClass' => $this->successCssClass,
'validatingCssClass' => $this->validatingCssClass,
'ajaxVar' => $this->ajaxVar,
- );
+ ];
if ($this->validationUrl !== null) {
$options['validationUrl'] = Html::url($this->validationUrl);
}
+ foreach (['beforeSubmit', 'beforeValidate', 'afterValidate'] as $name) {
+ if (($value = $this->$name) !== null) {
+ $options[$name] = $value instanceof JsExpression ? $value : new JsExpression($value);
+ }
+ }
return $options;
}
@@ -173,15 +212,15 @@ class ActiveForm extends Widget
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
* @return string the generated error summary
*/
- public function errorSummary($models, $options = array())
+ public function errorSummary($models, $options = [])
{
if (!is_array($models)) {
- $models = array($models);
+ $models = [$models];
}
- $lines = array();
+ $lines = [];
foreach ($models as $model) {
- /** @var $model Model */
+ /** @var Model $model */
foreach ($model->getFirstErrors() as $error) {
$lines[] = Html::encode($error);
}
@@ -218,12 +257,101 @@ class ActiveForm extends Widget
* @return ActiveField the created ActiveField object
* @see fieldConfig
*/
- public function field($model, $attribute, $options = array())
+ public function field($model, $attribute, $options = [])
{
- return Yii::createObject(array_merge($this->fieldConfig, $options, array(
+ return Yii::createObject(array_merge($this->fieldConfig, $options, [
'model' => $model,
'attribute' => $attribute,
'form' => $this,
- )));
+ ]));
+ }
+
+ /**
+ * Validates one or several models and returns an error message array indexed by the attribute IDs.
+ * This is a helper method that simplifies the way of writing AJAX validation code.
+ *
+ * For example, you may use the following code in a controller action to respond
+ * to an AJAX validation request:
+ *
+ * ~~~
+ * $model = new Post;
+ * $model->load($_POST);
+ * if (Yii::$app->request->isAjax) {
+ * Yii::$app->response->format = Response::FORMAT_JSON;
+ * return ActiveForm::validate($model);
+ * }
+ * // ... respond to non-AJAX request ...
+ * ~~~
+ *
+ * To validate multiple models, simply pass each model as a parameter to this method, like
+ * the following:
+ *
+ * ~~~
+ * ActiveForm::validate($model1, $model2, ...);
+ * ~~~
+ *
+ * @param Model $model the model to be validated
+ * @param mixed $attributes list of attributes that should be validated.
+ * If this parameter is empty, it means any attribute listed in the applicable
+ * validation rules should be validated.
+ *
+ * When this method is used to validate multiple models, this parameter will be interpreted
+ * as a model.
+ *
+ * @return array the error message array indexed by the attribute IDs.
+ */
+ public static function validate($model, $attributes = null)
+ {
+ $result = [];
+ if ($attributes instanceof Model) {
+ // validating multiple models
+ $models = func_get_args();
+ $attributes = null;
+ } else {
+ $models = [$model];
+ }
+ /** @var Model $model */
+ foreach ($models as $model) {
+ $model->validate($attributes);
+ foreach ($model->getErrors() as $attribute => $errors) {
+ $result[Html::getInputId($model, $attribute)] = $errors;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Validates an array of model instances and returns an error message array indexed by the attribute IDs.
+ * This is a helper method that simplifies the way of writing AJAX validation code for tabular input.
+ *
+ * For example, you may use the following code in a controller action to respond
+ * to an AJAX validation request:
+ *
+ * ~~~
+ * // ... load $models ...
+ * if (Yii::$app->request->isAjax) {
+ * Yii::$app->response->format = Response::FORMAT_JSON;
+ * return ActiveForm::validateMultiple($models);
+ * }
+ * // ... respond to non-AJAX request ...
+ * ~~~
+ *
+ * @param array $models an array of models to be validated.
+ * @param mixed $attributes list of attributes that should be validated.
+ * If this parameter is empty, it means any attribute listed in the applicable
+ * validation rules should be validated.
+ * @return array the error message array indexed by the attribute IDs.
+ */
+ public static function validateMultiple($models, $attributes = null)
+ {
+ $result = [];
+ /** @var Model $model */
+ foreach ($models as $i => $model) {
+ $model->validate($attributes);
+ foreach ($model->getErrors() as $attribute => $errors) {
+ $result[Html::getInputId($model, "[$i]" . $attribute)] = $errors;
+ }
+ }
+ return $result;
}
}
diff --git a/framework/yii/widgets/ActiveFormAsset.php b/framework/yii/widgets/ActiveFormAsset.php
new file mode 100644
index 0000000..5acb5e1
--- /dev/null
+++ b/framework/yii/widgets/ActiveFormAsset.php
@@ -0,0 +1,24 @@
+
+ * @since 2.0
+ */
+class ActiveFormAsset extends AssetBundle
+{
+ public $sourcePath = '@yii/assets';
+ public $js = [
+ 'yii.activeForm.js',
+ ];
+ public $depends = [
+ 'yii\web\YiiAsset',
+ ];
+}
diff --git a/framework/yii/widgets/BaseListView.php b/framework/yii/widgets/BaseListView.php
new file mode 100644
index 0000000..4c4e5a4
--- /dev/null
+++ b/framework/yii/widgets/BaseListView.php
@@ -0,0 +1,215 @@
+
+ * @since 2.0
+ */
+abstract class BaseListView extends Widget
+{
+ /**
+ * @var array the HTML attributes for the container tag of the list view.
+ * The "tag" element specifies the tag name of the container element and defaults to "div".
+ */
+ public $options = [];
+ /**
+ * @var \yii\data\DataProviderInterface the data provider for the view. This property is required.
+ */
+ public $dataProvider;
+ /**
+ * @var array the configuration for the pager widget. By default, [[LinkPager]] will be
+ * used to render the pager. You can use a different widget class by configuring the "class" element.
+ */
+ public $pager = [];
+ /**
+ * @var array the configuration for the sorter widget. By default, [[LinkSorter]] will be
+ * used to render the sorter. You can use a different widget class by configuring the "class" element.
+ */
+ public $sorter = [];
+ /**
+ * @var string the HTML content to be displayed as the summary of the list view.
+ * If you do not want to show the summary, you may set it with an empty string.
+ *
+ * The following tokens will be replaced with the corresponding values:
+ *
+ * - `{begin}`: the starting row number (1-based) currently being displayed
+ * - `{end}`: the ending row number (1-based) currently being displayed
+ * - `{count}`: the number of rows currently being displayed
+ * - `{totalCount}`: the total number of rows available
+ * - `{page}`: the page number (1-based) current being displayed
+ * - `{pageCount}`: the number of pages available
+ */
+ public $summary;
+ /**
+ * @var boolean whether to show the list view if [[dataProvider]] returns no data.
+ */
+ public $showOnEmpty = false;
+ /**
+ * @var string the HTML content to be displayed when [[dataProvider]] does not have any data.
+ */
+ public $emptyText;
+ /**
+ * @var string the layout that determines how different sections of the list view should be organized.
+ * The following tokens will be replaced with the corresponding section contents:
+ *
+ * - `{summary}`: the summary section. See [[renderSummary()]].
+ * - `{items}`: the list items. See [[renderItems()]].
+ * - `{sorter}`: the sorter. See [[renderSorter()]].
+ * - `{pager}`: the pager. See [[renderPager()]].
+ */
+ public $layout = "{summary}\n{items}\n{pager}";
+
+
+ /**
+ * Renders the data models.
+ * @return string the rendering result.
+ */
+ abstract public function renderItems();
+
+ /**
+ * Initializes the view.
+ */
+ public function init()
+ {
+ if ($this->dataProvider === null) {
+ throw new InvalidConfigException('The "dataProvider" property must be set.');
+ }
+ if ($this->emptyText === null) {
+ $this->emptyText = Yii::t('yii', 'No results found.');
+ }
+ $this->dataProvider->prepare();
+ }
+
+ /**
+ * Runs the widget.
+ */
+ public function run()
+ {
+ if ($this->dataProvider->getCount() > 0 || $this->showOnEmpty) {
+ $content = preg_replace_callback("/{\\w+}/", function ($matches) {
+ $content = $this->renderSection($matches[0]);
+ return $content === false ? $matches[0] : $content;
+ }, $this->layout);
+ } else {
+ $content = $this->renderEmpty();
+ }
+ $tag = ArrayHelper::remove($this->options, 'tag', 'div');
+ echo Html::tag($tag, $content, $this->options);
+ }
+
+ /**
+ * Renders a section of the specified name.
+ * If the named section is not supported, false will be returned.
+ * @param string $name the section name, e.g., `{summary}`, `{items}`.
+ * @return string|boolean the rendering result of the section, or false if the named section is not supported.
+ */
+ public function renderSection($name)
+ {
+ switch ($name) {
+ case '{summary}':
+ return $this->renderSummary();
+ case '{items}':
+ return $this->renderItems();
+ case '{pager}':
+ return $this->renderPager();
+ case '{sorter}':
+ return $this->renderSorter();
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Renders the HTML content indicating that the list view has no data.
+ * @return string the rendering result
+ * @see emptyText
+ */
+ public function renderEmpty()
+ {
+ return '' . ($this->emptyText === null ? Yii::t('yii', 'No results found.') : $this->emptyText) . '
';
+ }
+
+ /**
+ * Renders the summary text.
+ */
+ public function renderSummary()
+ {
+ $count = $this->dataProvider->getCount();
+ if ($count <= 0) {
+ return '';
+ }
+ if (($pagination = $this->dataProvider->getPagination()) !== false) {
+ $totalCount = $this->dataProvider->getTotalCount();
+ $begin = $pagination->getPage() * $pagination->pageSize + 1;
+ $end = $begin + $count - 1;
+ if ($begin > $end) {
+ $begin = $end;
+ }
+ $page = $pagination->getPage() + 1;
+ $pageCount = $pagination->pageCount;
+ if (($summaryContent = $this->summary) === null) {
+ $summaryContent = ''
+ . Yii::t('yii', 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.')
+ . '
';
+ }
+ } else {
+ $begin = $page = $pageCount = 1;
+ $end = $totalCount = $count;
+ if (($summaryContent = $this->summary) === null) {
+ $summaryContent = '' . Yii::t('yii', 'Total {count, number} {count, plural, one{item} other{items}}.') . '
';
+ }
+ }
+ return Yii::$app->getI18n()->format($summaryContent, [
+ 'begin' => $begin,
+ 'end' => $end,
+ 'count' => $count,
+ 'totalCount' => $totalCount,
+ 'page' => $page,
+ 'pageCount' => $pageCount,
+ ], Yii::$app->language);
+ }
+
+ /**
+ * Renders the pager.
+ * @return string the rendering result
+ */
+ public function renderPager()
+ {
+ $pagination = $this->dataProvider->getPagination();
+ if ($pagination === false || $this->dataProvider->getCount() <= 0) {
+ return '';
+ }
+ /** @var LinkPager $class */
+ $class = ArrayHelper::remove($this->pager, 'class', LinkPager::className());
+ $this->pager['pagination'] = $pagination;
+ return $class::widget($this->pager);
+ }
+
+ /**
+ * Renders the sorter.
+ * @return string the rendering result
+ */
+ public function renderSorter()
+ {
+ $sort = $this->dataProvider->getSort();
+ if ($sort === false || empty($sort->attributes) || $this->dataProvider->getCount() <= 0) {
+ return '';
+ }
+ /** @var LinkSorter $class */
+ $class = ArrayHelper::remove($this->sorter, 'class', LinkSorter::className());
+ $this->sorter['sort'] = $sort;
+ return $class::widget($this->sorter);
+ }
+}
diff --git a/framework/yii/widgets/Breadcrumbs.php b/framework/yii/widgets/Breadcrumbs.php
index 9214f86..2353845 100644
--- a/framework/yii/widgets/Breadcrumbs.php
+++ b/framework/yii/widgets/Breadcrumbs.php
@@ -23,12 +23,12 @@ use yii\helpers\Html;
*
* ~~~
* // $this is the view object currently being used
- * echo Breadcrumbs::widget(array(
- * 'links' => array(
- * array('label' => 'Sample Post', 'url' => array('post/edit', 'id' => 1)),
+ * echo Breadcrumbs::widget([
+ * 'links' => [
+ * ['label' => 'Sample Post', 'url' => ['post/edit', 'id' => 1]],
* 'Edit',
- * ),
- * ));
+ * ],
+ * ]);
* ~~~
*
* Because breadcrumbs usually appears in nearly every page of a website, you may consider placing it in a layout view.
@@ -37,9 +37,9 @@ use yii\helpers\Html;
*
* ~~~
* // $this is the view object currently being used
- * echo Breadcrumbs::widget(array(
- * 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(),
- * ));
+ * echo Breadcrumbs::widget([
+ * 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
+ * ]);
* ~~~
*
* @author Qiang Xue
@@ -54,7 +54,7 @@ class Breadcrumbs extends Widget
/**
* @var array the HTML attributes for the breadcrumb container tag.
*/
- public $options = array('class' => 'breadcrumb');
+ public $options = ['class' => 'breadcrumb'];
/**
* @var boolean whether to HTML-encode the link labels.
*/
@@ -71,21 +71,21 @@ class Breadcrumbs extends Widget
* with the following structure:
*
* ~~~
- * array(
+ * [
* 'label' => 'label of the link', // required
* 'url' => 'url of the link', // optional, will be processed by Html::url()
- * )
+ * ]
* ~~~
*
- * If a link is active, you only need to specify its "label", and instead of writing `array('label' => $label)`,
+ * If a link is active, you only need to specify its "label", and instead of writing `['label' => $label]`,
* you should simply use `$label`.
*/
- public $links = array();
+ public $links = [];
/**
* @var string the template used to render each inactive item in the breadcrumbs. The token `{link}`
* will be replaced with the actual HTML link for each inactive item.
*/
- public $itemTemplate = "{link} / \n";
+ public $itemTemplate = "{link} \n";
/**
* @var string the template used to render each active item in the breadcrumbs. The token `{link}`
* will be replaced with the actual HTML link for each active item.
@@ -100,18 +100,18 @@ class Breadcrumbs extends Widget
if (empty($this->links)) {
return;
}
- $links = array();
+ $links = [];
if ($this->homeLink === null) {
- $links[] = $this->renderItem(array(
+ $links[] = $this->renderItem([
'label' => Yii::t('yii', 'Home'),
'url' => Yii::$app->homeUrl,
- ), $this->itemTemplate);
+ ], $this->itemTemplate);
} elseif ($this->homeLink !== false) {
$links[] = $this->renderItem($this->homeLink, $this->itemTemplate);
}
foreach ($this->links as $link) {
if (!is_array($link)) {
- $link = array('label' => $link);
+ $link = ['label' => $link];
}
$links[] = $this->renderItem($link, isset($link['url']) ? $this->itemTemplate : $this->activeItemTemplate);
}
@@ -133,9 +133,9 @@ class Breadcrumbs extends Widget
throw new InvalidConfigException('The "label" element is required for each link.');
}
if (isset($link['url'])) {
- return strtr($template, array('{link}' => Html::a($label, $link['url'])));
+ return strtr($template, ['{link}' => Html::a($label, $link['url'])]);
} else {
- return strtr($template, array('{link}' => $label));
+ return strtr($template, ['{link}' => $label]);
}
}
}
diff --git a/framework/yii/widgets/Captcha.php b/framework/yii/widgets/Captcha.php
deleted file mode 100644
index ffe8802..0000000
--- a/framework/yii/widgets/Captcha.php
+++ /dev/null
@@ -1,140 +0,0 @@
-
- * @since 2.0
- */
-class Captcha extends InputWidget
-{
- /**
- * @var string the route of the action that generates the CAPTCHA images.
- * The action represented by this route must be an action of [[CaptchaAction]].
- */
- public $captchaAction = 'site/captcha';
- /**
- * @var array HTML attributes to be applied to the text input field.
- */
- public $options = array();
- /**
- * @var array HTML attributes to be applied to the CAPTCHA image tag.
- */
- public $imageOptions = array();
- /**
- * @var string the template for arranging the CAPTCHA image tag and the text input tag.
- * In this template, the token `{image}` will be replaced with the actual image tag,
- * while `{input}` will be replaced with the text input tag.
- */
- public $template = '{image} {input}';
-
- /**
- * Initializes the widget.
- */
- public function init()
- {
- parent::init();
-
- $this->checkRequirements();
-
- if (!isset($this->options['id'])) {
- $this->options['id'] = $this->hasModel() ? Html::getInputId($this->model, $this->attribute) : $this->getId();
- }
- if (!isset($this->imageOptions['id'])) {
- $this->imageOptions['id'] = $this->options['id'] . '-image';
- }
- }
-
- /**
- * Renders the widget.
- */
- public function run()
- {
- $this->registerClientScript();
- if ($this->hasModel()) {
- $input = Html::activeTextInput($this->model, $this->attribute, $this->options);
- } else {
- $input = Html::textInput($this->name, $this->value, $this->options);
- }
- $url = Yii::$app->getUrlManager()->createUrl($this->captchaAction, array('v' => uniqid()));
- $image = Html::img($url, $this->imageOptions);
- echo strtr($this->template, array(
- '{input}' => $input,
- '{image}' => $image,
- ));
- }
-
- /**
- * Registers the needed JavaScript.
- */
- public function registerClientScript()
- {
- $options = $this->getClientOptions();
- $options = empty($options) ? '' : Json::encode($options);
- $id = $this->imageOptions['id'];
- $this->getView()->registerAssetBundle('yii/captcha');
- $this->getView()->registerJs("jQuery('#$id').yiiCaptcha($options);");
- }
-
- /**
- * Returns the options for the captcha JS widget.
- * @return array the options
- */
- protected function getClientOptions()
- {
- $options = array(
- 'refreshUrl' => Html::url(array($this->captchaAction, CaptchaAction::REFRESH_GET_VAR => 1)),
- 'hashKey' => "yiiCaptcha/{$this->captchaAction}",
- );
- return $options;
- }
-
- /**
- * Checks if there is graphic extension available to generate CAPTCHA images.
- * This method will check the existence of ImageMagick and GD extensions.
- * @return string the name of the graphic extension, either "imagick" or "gd".
- * @throws InvalidConfigException if neither ImageMagick nor GD is installed.
- */
- public static function checkRequirements()
- {
- if (extension_loaded('imagick')) {
- $imagick = new \Imagick();
- $imagickFormats = $imagick->queryFormats('PNG');
- if (in_array('PNG', $imagickFormats)) {
- return 'imagick';
- }
- }
- if (extension_loaded('gd')) {
- $gdInfo = gd_info();
- if (!empty($gdInfo['FreeType Support'])) {
- return 'gd';
- }
- }
- throw new InvalidConfigException('GD with FreeType or ImageMagick PHP extensions are required.');
- }
-}
diff --git a/framework/yii/widgets/ContentDecorator.php b/framework/yii/widgets/ContentDecorator.php
index f91117a..9224f35 100644
--- a/framework/yii/widgets/ContentDecorator.php
+++ b/framework/yii/widgets/ContentDecorator.php
@@ -24,7 +24,7 @@ class ContentDecorator extends Widget
/**
* @var array the parameters (name => value) to be extracted and made available in the decorative view.
*/
- public $params = array();
+ public $params = [];
/**
* Starts recording a clip.
diff --git a/framework/yii/widgets/DetailView.php b/framework/yii/widgets/DetailView.php
new file mode 100644
index 0000000..8ced307
--- /dev/null
+++ b/framework/yii/widgets/DetailView.php
@@ -0,0 +1,211 @@
+ $model,
+ * 'attributes' => [
+ * 'title', // title attribute (in plain text)
+ * 'description:html', // description attribute in HTML
+ * [ // the owner name of the model
+ * 'label' => 'Owner',
+ * 'value' => $model->owner->name,
+ * ],
+ * ],
+ * ]);
+ * ~~~
+ *
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class DetailView extends Widget
+{
+ /**
+ * @var array|object the data model whose details are to be displayed. This can be either a [[Model]] instance
+ * or an associative array.
+ */
+ public $model;
+ /**
+ * @var array a list of attributes to be displayed in the detail view. Each array element
+ * represents the specification for displaying one particular attribute.
+ *
+ * An attribute can be specified as a string in the format of "Name" or "Name:Format", where "Name" refers to
+ * the attribute name, and "Format" represents the format of the attribute. The "Format" is passed to the [[Formatter::format()]]
+ * method to format an attribute value into a displayable text. Please refer to [[Formatter]] for the supported types.
+ *
+ * An attribute can also be specified in terms of an array with the following elements:
+ *
+ * - name: the attribute name. This is required if either "label" or "value" is not specified.
+ * - label: the label associated with the attribute. If this is not specified, it will be generated from the attribute name.
+ * - value: the value to be displayed. If this is not specified, it will be retrieved from [[model]] using the attribute name
+ * by calling [[ArrayHelper::getValue()]]. Note that this value will be formatted into a displayable text
+ * according to the "format" option.
+ * - format: the type of the value that determines how the value would be formatted into a displayable text.
+ * Please refer to [[Formatter]] for supported types.
+ * - visible: whether the attribute is visible. If set to `false`, the attribute will NOT be displayed.
+ */
+ public $attributes;
+ /**
+ * @var string|callback the template used to render a single attribute. If a string, the token `{label}`
+ * and `{value}` will be replaced with the label and the value of the corresponding attribute.
+ * If a callback (e.g. an anonymous function), the signature must be as follows:
+ *
+ * ~~~
+ * function ($attribute, $index, $widget)
+ * ~~~
+ *
+ * where `$attribute` refer to the specification of the attribute being rendered, `$index` is the zero-based
+ * index of the attribute in the [[attributes]] array, and `$widget` refers to this widget instance.
+ */
+ public $template = "