function createFile(fileInfo, rootDir) {
    let {path: relPath, fullPath, data, isVirtual, isScript, isStyle, isTemplate} = fileInfo;
    if (relPath && !fullPath) {
        fullPath = path.resolve(rootDir, relPath);
    }

    let extname = path.extname(fullPath).slice(1).toLowerCase();
    let vf = {
        processor: FILE_EXT_PROCESSOR[extname],
        dirname: path.dirname(fullPath),
        extname,
        fullPath,
        path: relPath || relative(fullPath, rootDir),
        rawContent: data,
        isVirtual,
        isScript: isScript == null ? isScriptType(extname) : isScript,
        isStyle: isStyle == null ? isStyleType(extname) : isStyle,
        isImg: isImgType(extname),
        isTpl: isTemplate == null ? isTplType(extname) : isTemplate,
        isJson: isJsonType(extname),
        addDeps,
        addSubFile,
        reset: resetFile,
        sourceMap: null
    };

    Object.defineProperties(vf, {
        stream: {
            get: getFileStream
        },
        content: {
            get: loadFileContent,
            set: updateFileContent
        }
    });

    return vf;
}

function createFile ( fileInfo , rootDir ) { let { path : relPath , fullPath , data , isVirtual , isScript , isStyle , isTemplate } = fileInfo ; if ( relPath && ! fullPath ) { fullPath = path . resolve ( rootDir , relPath ) ; } let extname = path . extname ( fullPath ) . slice ( 1 ) . toLowerCase ( ) ; let vf = { processor : FILE_EXT_PROCESSOR [ extname ] , dirname : path . dirname ( fullPath ) , extname , fullPath , path : relPath || relative ( fullPath , rootDir ) , rawContent : data , isVirtual , isScript : isScript == null ? isScriptType ( extname ) : isScript , isStyle : isStyle == null ? isStyleType ( extname ) : isStyle , isImg : isImgType ( extname ) , isTpl : isTemplate == null ? isTplType ( extname ) : isTemplate , isJson : isJsonType ( extname ) , addDeps , addSubFile , reset : resetFile , sourceMap : null } ; Object . defineProperties ( vf , { stream : { get : getFileStream } , content : { get : loadFileContent , set : updateFileContent } } ) ; return vf ; }

Create file info object

@param {Object} fileInfo the file to create
@param {string=} fileInfo.fullPath the file absolute path
@param {string=} fileInfo.path the relative path relative to rootDir
@param {boolean=} fileInfo.isVirtual whether a virtual file, optional, by default false
@param {*=} fileInfo.data the file content, optional
@param {string} rootDir the root directory
@return {Object}


function tryToResolveRequireModId(t, path, state) {
    let opts = state.opts || {};
    let resolveRequireId = opts.resolveDepRequireId;

    let exportNode = path.node;
    let source = exportNode.source;
    if (!t.isStringLiteral(source)) {
        return;
    }

    let modId = source.value;
    let newModId = resolveRequireId(modId);
    if (newModId && newModId !== modId) {
        let requireIdNode = path.get('source');
        requireIdNode.replaceWith(
            t.stringLiteral(newModId)
        );
    }
}

function tryToResolveRequireModId ( t , path , state ) { let opts = state . opts || { } ; let resolveRequireId = opts . resolveDepRequireId ; let exportNode = path . node ; let source = exportNode . source ; if ( ! t . isStringLiteral ( source ) ) { return ; } let modId = source . value ; let newModId = resolveRequireId ( modId ) ; if ( newModId && newModId !== modId ) { let requireIdNode = path . get ( 'source' ) ; requireIdNode . replaceWith ( t . stringLiteral ( newModId ) ) ; } }

Resolve the required module id and replace the old module id using the
resolved module id.

@inner
@param {Object} t the babel type definition
@param {Object} path the node path
@param {Object} state the transformation plugin state

Resolve the required module id and replace the old module id using the resolved module id .


function showEventNameLog(name, newName, eventModifiers, attrs, tplOpts) {
    let {logger, file, appType} = tplOpts;

    NOT_SUPPORT_MODIFIERS.forEach(item => {
        if (eventModifiers.includes(item)) {
            logger.warn(
                `${file.path} template event attribute ${name}`,
                `is not support with ${item} modifier in ${appType} env`
            );
        }
    });

    if (attrs.hasOwnProperty(newName)) {
        logger.warn(`${file.path} template attribute ${name} is conflicted with ${newName}`);
    }
}

function showEventNameLog ( name , newName , eventModifiers , attrs , tplOpts ) { let { logger , file , appType } = tplOpts ; NOT_SUPPORT_MODIFIERS . forEach ( item => { if ( eventModifiers . includes ( item ) ) { logger . warn ( ` ${ file . path } ${ name } ` , ` ${ item } ${ appType } ` ) ; } } ) ; if ( attrs . hasOwnProperty ( newName ) ) { logger . warn ( ` ${ file . path } ${ name } ${ newName } ` ) ; } }

show warning log when parsing event name

@param {string} name     attribute name
@param {string} newName  new attribute name
@param {Array} eventModifiers     array of event modifier
@param {Object} attrs     all attributes
@param {Object} tplOpts  template options


function getRandomStringNotIn(string) {
    const randomString = Math.random().toString(36).substr(2);
    if (string.indexOf(randomString) === -1) {
        return randomString;
    }
    return getRandomStringNotIn(string);
}

function getRandomStringNotIn ( string ) { const randomString = Math . random ( ) . toString ( 36 ) . substr ( 2 ) ; if ( string . indexOf ( randomString ) === - 1 ) { return randomString ; } return getRandomStringNotIn ( string ) ; }

get a random string which is not in the string

@param {string} string the parameter string
@return {string} the random string


function sortDefaultProcessors(processorNames, processors) {
    if (Array.isArray(processorNames)) {
        processorNames.sort((a, b) => {
            a = processors[a];
            b = processors[b];
            return (a.order || 0) - (b.order || 0);
        });
    }
}

function sortDefaultProcessors ( processorNames , processors ) { if ( Array . isArray ( processorNames ) ) { processorNames . sort ( ( a , b ) => { a = processors [ a ] ; b = processors [ b ] ; return ( a . order || 0 ) - ( b . order || 0 ) ; } ) ; } }

Sort the file default processors by the processor order property.

@inner
@param {Array.<string>|string} processorNames the processor names.
@param {Object} processors the builtin processors, the key is processor name,
the value is the processor config information.

Sort the file default processors by the processor order property .


function addFileExtnameAssociatedProcessor(extnames, processorName, existedMap) {
    if (!extnames) {
        return;
    }

    if (!Array.isArray(extnames)) {
        extnames = [extnames];
    }

    extnames.forEach(k => {
        k = k.toLowerCase();

        let processors = existedMap[k];
        if (Array.isArray(processors)) {
            processors.push(processorName);
        }
        else if (processors) {
            existedMap[k] = [processors, processorName];
        }
        else {
            existedMap[k] = processorName;
        }
    });
}

function addFileExtnameAssociatedProcessor ( extnames , processorName , existedMap ) { if ( ! extnames ) { return ; } if ( ! Array . isArray ( extnames ) ) { extnames = [ extnames ] ; } extnames . forEach ( k => { k = k . toLowerCase ( ) ; let processors = existedMap [ k ] ; if ( Array . isArray ( processors ) ) { processors . push ( processorName ) ; } else if ( processors ) { existedMap [ k ] = [ processors , processorName ] ; } else { existedMap [ k ] = processorName ; } } ) ; }

Add the file extname associated processors.

@inner
@param {string|Array.<string>} extnames the file extnames
@param {string} processorName the processor name
@param {Object} existedMap the existed file extname associated processors map

Add the file extname associated processors .


function removeFileExtnameAssociatedProcessor(extnames, processorName, existedMap) {
    if (!extnames) {
        return;
    }

    if (!Array.isArray(extnames)) {
        extnames = [extnames];
    }

    extnames.forEach(k => {
        let currItems = existedMap[k];
        if (Array.isArray(currItems)) {
            let idx = currItems.indexOf(processorName);
            if (idx !== -1) {
                currItems.splice(idx, 1);
                currItems.length === 0 && (existedMap[k] = undefined);
            }
        }
        else if (currItems === processorName) {
            existedMap[k] = undefined;
        }
    });
}

function removeFileExtnameAssociatedProcessor ( extnames , processorName , existedMap ) { if ( ! extnames ) { return ; } if ( ! Array . isArray ( extnames ) ) { extnames = [ extnames ] ; } extnames . forEach ( k => { let currItems = existedMap [ k ] ; if ( Array . isArray ( currItems ) ) { let idx = currItems . indexOf ( processorName ) ; if ( idx !== - 1 ) { currItems . splice ( idx , 1 ) ; currItems . length === 0 && ( existedMap [ k ] = undefined ) ; } } else if ( currItems === processorName ) { existedMap [ k ] = undefined ; } } ) ; }

Remove the file extname associated processors.

@inner
@param {string|Array.<string>} extnames the file extnames
@param {string} processorName the processor name
@param {Object} existedMap the existed file extname associated processors map

Remove the file extname associated processors .


function getFileExtnameAssociatedProcessor(processors) {
    let result = Object.keys(processors).reduce(
        (lastValue, processorName) => {
            let processor = processors[processorName];
            let extnames = processor.extnames || [];
            if (!Array.isArray(extnames)) {
                extnames = [extnames];
            }
            addFileExtnameAssociatedProcessor(extnames, processorName, lastValue);
            return lastValue;
        },
        {}
    );

    Object.keys(result).forEach(
        k => sortDefaultProcessors(result[k], processors)
    );
    return result;
}

function getFileExtnameAssociatedProcessor ( processors ) { let result = Object . keys ( processors ) . reduce ( ( lastValue , processorName ) => { let processor = processors [ processorName ] ; let extnames = processor . extnames || [ ] ; if ( ! Array . isArray ( extnames ) ) { extnames = [ extnames ] ; } addFileExtnameAssociatedProcessor ( extnames , processorName , lastValue ) ; return lastValue ; } , { } ) ; Object . keys ( result ) . forEach ( k => sortDefaultProcessors ( result [ k ] , processors ) ) ; return result ; }

Get the map of the file extname with processor name.

@inner
@param {Object} processors the builtin processors, the key is processor name,
the value is the processor config information.
@return {Object}

Get the map of the file extname with processor name .


function initProcessorInfo(name, info, existedProcessors) {
    let processor = info.processor;
    if (!processor) {
        return info;
    }

    if (typeof processor === 'string') {
        // if the processor refer to the existed processor, merge the existed
        // processor info with current processor
        let old = existedProcessors[processor];
        if (old) {
            info.processor = undefined;
            // remove undefined attributes
            info = removeUndefinedAttributes(info);

            let deps = info.deps;
            deps && !Array.isArray(deps) && (deps = [deps]);

            let oldDeps = old.deps;
            oldDeps && !Array.isArray(oldDeps) && (oldDeps = [oldDeps]);

            info = Object.assign({}, old, info);
            info.deps = merge(deps || [], oldDeps || []);

            return Object.assign({refer: processor}, old, info);
        }
    }

    try {
        info.processor = resolveProcessor(processor);
    }
    catch (ex) {
        let msg = `processor \`${name}\` handler \`${processor}\` not `
            + `found: ${ex.message || ex.toString()}`;
        throw new Error(msg);
    }
    return info;
}

Initialize processor information

@inner
@param {string} name the processor name
@param {Object} info the processor info
@param {Object} existedProcessors the existed processors
@return {Object}


function overrideObjectFunctions(curr, old) {
    if (!old) {
        return curr;
    }

    if (!curr && curr !== undefined) {
        return old;
    }

    let result = {};
    Object.keys(curr).forEach(k => {
        let v = curr[k];
        let oldV = old[k];
        if (typeof v === 'function' && typeof oldV === 'function') {
            let currV = v;
            v = function (...args) {
                oldV.apply(this, args);
                currV.apply(this, args);
            };
        }
        result[k] = v;
    });

    return result;
}

function overrideObjectFunctions ( curr , old ) { if ( ! old ) { return curr ; } if ( ! curr && curr !== undefined ) { return old ; } let result = { } ; Object . keys ( curr ) . forEach ( k => { let v = curr [ k ] ; let oldV = old [ k ] ; if ( typeof v === 'function' && typeof oldV === 'function' ) { let currV = v ; v = function ( ... args ) { oldV . apply ( this , args ) ; currV . apply ( this , args ) ; } ; } result [ k ] = v ; } ) ; return result ; }

Override the function value for the given object

@param {?Object} curr the current object
@param {?Object} old the old object
@return {?Object}


function overrideProcessor(existedProcessor, extnameProcessorMap, opts) {
    let oldExtnames;
    let newExtnames;
    Object.keys(opts).forEach(k => {
        let v = opts[k];
        if (!v) {
            return;
        }

        if (k === 'extnames') {
            oldExtnames = existedProcessor[k];
            newExtnames = v;
        }

        if (k === 'hook') {
            v = overrideObjectFunctions(v, existedProcessor[k]);
        }

        existedProcessor[k] = v;
    });

    let processor = opts.processor;
    if (processor) {
        existedProcessor.processor = resolveProcessor(processor);
    }

    let processorName = opts.name;
    removeFileExtnameAssociatedProcessor(
        oldExtnames, processorName, extnameProcessorMap
    );
    addFileExtnameAssociatedProcessor(newExtnames, processorName, extnameProcessorMap);
}

function overrideProcessor ( existedProcessor , extnameProcessorMap , opts ) { let oldExtnames ; let newExtnames ; Object . keys ( opts ) . forEach ( k => { let v = opts [ k ] ; if ( ! v ) { return ; } if ( k === 'extnames' ) { oldExtnames = existedProcessor [ k ] ; newExtnames = v ; } if ( k === 'hook' ) { v = overrideObjectFunctions ( v , existedProcessor [ k ] ) ; } existedProcessor [ k ] = v ; } ) ; let processor = opts . processor ; if ( processor ) { existedProcessor . processor = resolveProcessor ( processor ) ; } let processorName = opts . name ; removeFileExtnameAssociatedProcessor ( oldExtnames , processorName , extnameProcessorMap ) ; addFileExtnameAssociatedProcessor ( newExtnames , processorName , extnameProcessorMap ) ; }

Override the builtin processor definition

@inner
@param {Object} existedProcessor the builtin existed processor config
@param {Object} extnameProcessorMap the file extname associated processors map
@param {Object} opts the override options


function updateReferProcessorInfo(existedProcessors, processorName, referProcessorName) {
    let currProcessor = existedProcessors[processorName];
    if (!currProcessor || currProcessor.processor) {
        return;
    }

    let referProcessorInfo = existedProcessors[referProcessorName];
    if (!referProcessorInfo) {
        return;
    }

    let deps = referProcessorInfo.deps;
    deps && !Array.isArray(deps) && (deps = [deps]);

    let oldDeps = currProcessor.deps;
    oldDeps && !Array.isArray(oldDeps) && (oldDeps = [oldDeps]);

    let old = Object.assign({}, currProcessor);
    Object.assign(currProcessor, referProcessorInfo, old, {
        refer: referProcessorName,
        deps: merge(deps || [], oldDeps || []),
        processor: referProcessorInfo.processor
    });
}

function updateReferProcessorInfo ( existedProcessors , processorName , referProcessorName ) { let currProcessor = existedProcessors [ processorName ] ; if ( ! currProcessor || currProcessor . processor ) { return ; } let referProcessorInfo = existedProcessors [ referProcessorName ] ; if ( ! referProcessorInfo ) { return ; } let deps = referProcessorInfo . deps ; deps && ! Array . isArray ( deps ) && ( deps = [ deps ] ) ; let oldDeps = currProcessor . deps ; oldDeps && ! Array . isArray ( oldDeps ) && ( oldDeps = [ oldDeps ] ) ; let old = Object . assign ( { } , currProcessor ) ; Object . assign ( currProcessor , referProcessorInfo , old , { refer : referProcessorName , deps : merge ( deps || [ ] , oldDeps || [ ] ) , processor : referProcessorInfo . processor } ) ; }

Update existed processor processor info. If the processor has been defined,
it'll skip the update.

@param {Object} existedProcessors the existed processors info
@param {string} processorName the processor name to update
@param {string} referProcessorName the refer processor

Update existed processor processor info . If the processor has been defined it ll skip the update .


function registerProcessor(existedProcessors, extnameProcessorMap, opts) {
    let {name, processor, deps, rext, extnames, options, order, hook} = opts;
    if (!name) {
        throw new Error('missing processor name to register');
    }

    if (existedProcessors.hasOwnProperty(name)) {
        // override existed processor definition
        overrideProcessor(
            existedProcessors[name], extnameProcessorMap, opts
        );
    }
    else {
        existedProcessors[name] = initProcessorInfo(name, {
            processor,
            deps,
            rext,
            extnames,
            options,
            order,
            hook
        }, existedProcessors);
        addFileExtnameAssociatedProcessor(extnames, name, extnameProcessorMap);
    }

    // resort the file extname associated processors execution order
    if (extnames && !Array.isArray(extnames)) {
        extnames = [extnames];
    }
    extnames && extnames.forEach(
        k => sortDefaultProcessors(extnameProcessorMap[k], existedProcessors)
    );
}

Register the custom processor

@param {Object} existedProcessors the existed processors, the key is processor name,
the value is the processor config information.
@param {Object} extnameProcessorMap the file extname associated processors map
@param {Object} opts the processor config


function generateFilterCode(filterObjAst, t, options) {
    if (!filterObjAst.properties.length) {
        return '';
    }

    let {format = 'es6', usingBabel6} = options || {};
    let ast;
    if (format === 'es6') {
        ast = generateES6ModuleFilterCode(filterObjAst, t);
    }
    else {
        ast = generateCommonJSModuleFilterCode(filterObjAst, t);
    }

    return generateCode(ast, {
        auxiliaryCommentBefore: 'Auto generated filter code by okam'
    }, usingBabel6).code;
}

function
generateFilterCode
(
filterObjAst
,
t
,
options
)
{
if
(
!
filterObjAst
.
properties
.
length
)
{
return
''
;
}
let
{
format
=
'es6'
,
usingBabel6
}
=
options
||
{
}
;
let
ast
;
if
(
format
===
'es6'
)
{
ast
=
generateES6ModuleFilterCode
(
filterObjAst
,
t
)
;
}
else
{
ast
=
generateCommonJSModuleFilterCode
(
filterObjAst
,
t
)
;
}
return
generateCode
(
ast
,
{
auxiliaryCommentBefore
:
'Auto generated filter code by okam'
}
,
usingBabel6
)
.
code
;
}

Generated filter code

@param {Object} filterObjAst the filter definition ast
@param {Object} t the babel type definition
@param {Object=} options the generation option
@param {string=} options.format the module format, by default es6 module syntax
the supported module format: es6/commonjs
@param {boolean=} options.usingBabel6 whether using babel6 processor, by default false
@return {string}


function createAPIDoneHook(done, ctx) {
    let hookInfo = {
        resData: null,
        resException: false,
        hook: null
    };

    hookInfo.hook = (err, res, complete, shouldCatchException) => {
        let result;
        if (err != null) {
            result = err;
            if (shouldCatchException) {
                try {
                    let data = done(result, null, ctx);
                    data == null || (result = data);
                }
                catch (ex) {
                    // cache exception info for promise
                    hookInfo.resException = true;
                    result = ex;
                }
            }
            else {
                let data = done(result, null, ctx);
                data == null || (result = data);
            }
        }
        else {
            let data = done(null, res, ctx);
            data == null || (res = data);
            result = res;
        }

        // cache response data for promise
        hookInfo.resData = result;

        complete && complete(result); // call complete callback

        return result;
    };

    return hookInfo;
}

function
createAPIDoneHook
(
done
,
ctx
)
{
let
hookInfo
=
{
resData
:
,
resException
:
false
,
hook
:
}
;
hookInfo
.
hook
=
(
err
,
res
,
complete
,
shouldCatchException
)
=>
{
let
result
;
if
(
err
!=
)
{
result
=
err
;
if
(
shouldCatchException
)
{
try
{
let
data
=
done
(
result
,
,
ctx
)
;
data
==
||
(
result
=
data
)
;
}
catch
(
ex
)
{
// cache exception info for promise
hookInfo
.
resException
=
true
;
result
=
ex
;
}
}
else
{
let
data
=
done
(
result
,
,
ctx
)
;
data
==
||
(
result
=
data
)
;
}
}
else
{
let
data
=
done
(
,
res
,
ctx
)
;
data
==
||
(
res
=
data
)
;
result
=
res
;
}
// cache response data for promise
hookInfo
.
resData
=
result
;
complete
&&
complete
(
result
)
;
// call complete callback
return
result
;
}
;
return
hookInfo
;
}

Crate API done hook

@inner
@param {Function} done the done hook to execute
@param {Object} ctx the extra call context for the hook
@return {Object}


function interceptPromiseResponse(hasIntercepted, promise, doneHookInfo) {
    let {hook} = doneHookInfo;
    return promise.then(
        res => (hasIntercepted ? doneHookInfo.resData : hook(null, res)),
        err => {
            // check whether is intercepted to avoid repeated interception
            if (hasIntercepted) {
                let {resException, resData} = doneHookInfo;
                if (resException) {
                    throw resData;
                }
                return resData;
            }

            return hook(err || /* istanbul ignore next */ 'err');
        }
    );
}

Intercept the API promise response

@inner
@param {boolean} hasIntercepted whether intercepted by the API call arguments
@param {Promise} promise the promise response
@param {Object} doneHookInfo the done hook info
@return {Promise}


function hookAPIInit(init, args, ctx) {
    // API init interception
    if (isFunction(init)) {
        if (args.length > 1 || !isObject(args[0])) {
            // convert as one array type argument to make the developer
            // can modify the call arguments directly
            args = [args];
        }
        return init.apply(this, [...args, ctx]);
    }
}

Execute API initialization hook

@inner
@param {Function} init the initialization hook
@param {Array} args the API call arguments
@param {Object} ctx the extra call context for the hook
@return {*}


function hookAPIDone(done, sync, args, ctx) {
    // API done interception
    let hasDoneHook = isFunction(done);
    let hasIntercepted = false;
    let doneHookInfo;
    if (hasDoneHook) {
        doneHookInfo = createAPIDoneHook(done, ctx);
        // intercept async API based the args: `success`/`fail`/`complete` callback
        hasIntercepted = interceptAsyncAPIDone(sync, args, doneHookInfo.hook);
    }

    return {
        hasDoneHook,
        hasIntercepted,
        doneHookInfo
    };
}

Execute API done hook

@inner
@param {Function} done the done hook
@param {boolean} sync whether is sync API, if true, it will not hook the
API done result based the call arguments
@param {Array} args the API call arguments
@param {Object} ctx the extra call context for the hook
@return {Object} return the api done hook info


function queryComponentInstance(ctx, id) {
    let isSelectAll = Array.isArray(id);
    isSelectAll && (id = id[0]);

    // query multiple components is not supported
    let key = id.substr(1);
    return ctx.$child(key);
}

Query component instance by the given id

@inner
@param {Object} ctx the component context
@param {string} id the component reference id
@return {?Object}


function initRelations(component, isPage) {
    let relations = component.relations;
    if (!relations || isPage) {
        // relations only support component, page is not supported
        return;
    }

    // normalize relation `target` information
    Object.keys(relations).forEach(k => {
        let item = relations[k];
        let {target} = item;
        if (typeof target === 'function') {
            target = target();
            item.target = target.behavior;
        }
    });
}

Initialize the component relations

@inner
@param {Object} component the component info
@param {boolean} isPage whether is the page component


function preparePolyfillSupport(polyfill, rootDir, logger) {
    if (!polyfill) {
        return;
    }

    if (!Array.isArray(polyfill)) {
        polyfill = [polyfill];
    }

    polyfill.forEach(info => {
        try {
            ensure(info.desc, info.deps, rootDir);
        }
        catch (ex) {
            logger.warn(ex.toString());
        }
    });
}

function preparePolyfillSupport ( polyfill , rootDir , logger ) { if ( ! polyfill ) { return ; } if ( ! Array . isArray ( polyfill ) ) { polyfill = [ polyfill ] ; } polyfill . forEach ( info => { try { ensure ( info . desc , info . deps , rootDir ) ; } catch ( ex ) { logger . warn ( ex . toString ( ) ) ; } } ) ; }

Prepare the dependencies of the polyfill to support.

@param {Array.<string>|string} polyfill the polyfill type to support,
`promise` or `async` is currently support type
@param {string} rootDir the project root dir
@param {Object} logger log util

Prepare the dependencies of the polyfill to support .


function getPlainObjectNodeValue(node, path, t) {
    let result;
    if (t.isObjectExpression(node)) {
        result = {};

        let props = node.properties || [];
        for (let i = 0, len = props.length; i < len; i++) {
            let subNode = props[i];
            let keyNode = subNode.key;
            let key;
            if (t.isLiteral(keyNode)) {
                key = keyNode.value;
            }
            else if (t.isIdentifier(keyNode)) {
                key = keyNode.name;
            }

            if (!key) {
                continue;
            }

            result[key] = getPlainObjectNodeValue(subNode.value, path, t);
        }
    }
    else if (t.isArrayExpression(node)) {
        result = [];
        node.elements.forEach(item => {
            result.push(getPlainObjectNodeValue(item, path, t));
        });
    }
    else if (t.isNullLiteral(node)) {
        result = null;
    }
    else if (t.isLiteral(node)) {
        result = node.value;
    }
    else {
        throw path.buildCodeFrameError('only constant is supported');
    }
    return result;
}

function
getPlainObjectNodeValue
(
node
,
path
,
t
)
{
let
result
;
if
(
t
.
isObjectExpression
(
node
)
)
{
result
=
{
}
;
let
props
=
node
.
properties
||
[
]
;
for
(
let
i
=
0
,
len
=
props
.
length
;
i
<
len
;
i
++
)
{
let
subNode
=
props
[
i
]
;
let
keyNode
=
subNode
.
key
;
let
key
;
if
(
t
.
isLiteral
(
keyNode
)
)
{
key
=
keyNode
.
value
;
}
else
if
(
t
.
isIdentifier
(
keyNode
)
)
{
key
=
keyNode
.
name
;
}
if
(
!
key
)
{
continue
;
}
result
[
key
]
=
getPlainObjectNodeValue
(
subNode
.
value
,
path
,
t
)
;
}
}
else
if
(
t
.
isArrayExpression
(
node
)
)
{
result
=
[
]
;
node
.
elements
.
forEach
(
item
=>
{
result
.
push
(
getPlainObjectNodeValue
(
item
,
path
,
t
)
)
;
}
)
;
}
else
if
(
t
.
isNullLiteral
(
node
)
)
{
result
=
;
}
else
if
(
t
.
isLiteral
(
node
)
)
{
result
=
node
.
value
;
}
else
{
throw
path
.
buildCodeFrameError
(
'only constant is supported'
)
;
}
return
result
;
}

Convert the plain object node to javascript object

@inner
@param {Object} node the plain object node
@param {Object} path the node path
@param {Object} t the babel type definition
@return {Object}


function createNode(value, t) {
    if (t.isIdentifier(value)) {
        return value;
    }

    if (Array.isArray(value)) {
        let elements = [];
        value.forEach(item => {
            let node = createNode(item, t);
            node && elements.push(node);
        });

        return t.arrayExpression(elements);
    }

    if (Object.prototype.toString.call(value) === '[object Object]') {
        let props = [];
        Object.keys(value).forEach(k => {
            let node = createNode(value[k], t);
            if (node) {
                props.push(t.objectProperty(
                    t.identifier(`'${k}'`),
                    node
                ));
            }
        });

        return t.objectExpression(props);
    }

    if (value == null) {
        return t.nullLiteral();
    }

    let valueType = typeof value;
    switch (valueType) {
        case 'boolean':
            return t.booleanLiteral(value);
        case 'string':
            return t.stringLiteral(value);
        case 'number':
            return t.numericLiteral(value);
    }
}

function
createNode
(
value
,
t
)
{
if
(
t
.
isIdentifier
(
value
)
)
{
return
value
;
}
if
(
Array
.
isArray
(
value
)
)
{
let
elements
=
[
]
;
value
.
forEach
(
item
=>
{
let
node
=
createNode
(
item
,
t
)
;
node
&&
elements
.
push
(
node
)
;
}
)
;
return
t
.
arrayExpression
(
elements
)
;
}
if
(
Object
.
prototype
.
toString
.
call
(
value
)
===
'[object Object]'
)
{
let
props
=
[
]
;
Object
.
keys
(
value
)
.
forEach
(
k
=>
{
let
node
=
createNode
(
value
[
k
]
,
t
)
;
if
(
node
)
{
props
.
push
(
t
.
objectProperty
(
t
.
identifier
(
`
${
k
}
`
)
,
node
)
)
;
}
}
)
;
return
t
.
objectExpression
(
props
)
;
}
if
(
value
==
)
{
return
t
.
nullLiteral
(
)
;
}
let
valueType
=
typeof
value
;
switch
(
valueType
)
{
case
'boolean'
:
return
t
.
booleanLiteral
(
value
)
;
case
'string'
:
return
t
.
stringLiteral
(
value
)
;
case
'number'
:
return
t
.
numericLiteral
(
value
)
;
}
}

Create AST node by the given value

@inner
@param {*} value the value to create
@param {Object} t the babel type definition
@return {?Object}


function removeComments(t, path, type) {
    let commentPaths = path.get(type);
    if (!commentPaths || !commentPaths.length) {
        return;
    }

    let isLeadingType = type === LEADING_COMMENT_TYPE;
    if (isLeadingType) {
        let parentPath = path.parentPath;
        let isParentProgram = parentPath && t.isProgram(parentPath.node);

        // move leading comments to program
        if (isParentProgram) {
            parentPath.addComments(
                'leading',
                commentPaths.map(item => item.node)
            );
        }
    }

    commentPaths.forEach(item => item.remove());
}

function
removeComments
(
t
,
path
,
type
)
{
let
commentPaths
=
path
.
get
(
type
)
;
if
(
!
commentPaths
||
!
commentPaths
.
length
)
{
return
;
}
let
isLeadingType
=
type
===
LEADING_COMMENT_TYPE
;
if
(
isLeadingType
)
{
let
parentPath
=
path
.
parentPath
;
let
isParentProgram
=
parentPath
&&
t
.
isProgram
(
parentPath
.
node
)
;
// move leading comments to program
if
(
isParentProgram
)
{
parentPath
.
addComments
(
'leading'
,
commentPaths
.
map
(
item
=>
item
.
node
)
)
;
}
}
commentPaths
.
forEach
(
item
=>
item
.
remove
(
)
)
;
}

Remove node comments

@param {Object} t the babel type definition
@param {Object} path the node path
@param {string} type the comment type


function handleProxyEvent(source, target, eventName, options) {
    let {newEventName, prependSourceArg} = options || {};

    source.on(eventName, function (...args) {
        prependSourceArg && args.unshift(source);

        if (typeof newEventName === 'function') {
            newEventName.apply(target, args);
        }
        else {
            newEventName || (newEventName = eventName);
            args.unshift(newEventName);
            target.emit.apply(target, args);
        }
    });
}

function handleProxyEvent ( source , target , eventName , options ) { let { newEventName , prependSourceArg } = options || { } ; source . on ( eventName , function ( ... args ) { prependSourceArg && args . unshift ( source ) ; if ( typeof newEventName === 'function' ) { newEventName . apply ( target , args ) ; } else { newEventName || ( newEventName = eventName ) ; args . unshift ( newEventName ) ; target . emit . apply ( target , args ) ; } } ) ; }

Handle the event proxy

@inner
@param {EventEmitter} source the event trigger source
@param {EventEmitter} target the proxy target
@param {string} eventName the proxy event name
@param {Object=} options the proxy options
@param {string|Function=} options.newEventName the new event name to trigger
or default event handler
@param {boolean=} options.prependSourceArg whether prepend the original event
trigger to event handler argument list. By default, false.


function makeComputedObservable(ctx) {
    let computedInfo = ctx.$rawComputed || {};
    if (typeof computedInfo === 'function') {
        ctx.$rawComputed = computedInfo = computedInfo();
    }

    let observer = new ComputedObserver(ctx, computedInfo);
    let ctxProps = {};
    Object.keys(computedInfo).forEach(k => {
        ctxProps[k] = {
            get: observer.getGetter(k),
            set: observer.getSetter(k),
            enumerable: true
        };
    });
    Object.defineProperties(ctx, ctxProps);
    return observer;
}

function makeComputedObservable ( ctx ) { let computedInfo = ctx . $rawComputed || { } ; if ( typeof computedInfo === 'function' ) { ctx . $rawComputed = computedInfo = computedInfo ( ) ; } let observer = new ComputedObserver ( ctx , computedInfo ) ; let ctxProps = { } ; Object . keys ( computedInfo ) . forEach ( k => { ctxProps [ k ] = { get : observer . getGetter ( k ) , set : observer . getSetter ( k ) , enumerable : true } ; } ) ; Object . defineProperties ( ctx , ctxProps ) ; return observer ; }

Make computed props observable

@inner
@param {Object} ctx the component instance context
@return {Observer} the observer


function makePropsObservable(ctx) {
    let props = ctx.$rawProps;
    if (typeof props === 'function') {
        props = ctx.$rawProps = props();
    }

    if (!props) {
        return;
    }

    let observer = new Observer(
        ctx,
        ctx[propDataKey] || /* istanbul ignore next */ {},
        null,
        true
    );
    let propsObj = {};

    Object.keys(props).reduce((last, item) => {
        last[item] = true;
        return last;
    }, propsObj);
    Object.defineProperties(ctx, proxyObject(observer, propsObj));

    return observer;
}

function
makePropsObservable
(
ctx
)
{
let
props
=
ctx
.
$rawProps
;
if
(
typeof
props
===
'function'
)
{
props
=
ctx
.
$rawProps
=
props
(
)
;
}
if
(
!
props
)
{
return
;
}
let
observer
=
new
Observer
(
ctx
,
ctx
[
propDataKey
]
||
/* istanbul ignore next */
{
}
,
,
true
)
;
let
propsObj
=
{
}
;
Object
.
keys
(
props
)
.
reduce
(
(
last
,
item
)
=>
{
last
[
item
]
=
true
;
return
last
;
}
,
propsObj
)
;
Object
.
defineProperties
(
ctx
,
proxyObject
(
observer
,
propsObj
)
)
;
return
observer
;
}

Make props observable

@inner
@param {Object} ctx the component instance context
@return {Observer} the observer


function makeDataObservable(ctx) {
    const data = ctx.data;
    if (!data) {
        return;
    }

    if (isPlainObject(data)) {
        /* eslint-disable no-use-before-define */
        let observer = new Observer(ctx, data);
        Object.defineProperties(
            ctx, proxyObject(observer, data, ctx)
        );
        return observer;
    }

    let err = new Error('data require plain object');
    err.isTypeError = true;
    throw err;
}

Make data observable

@inner
@param {Object} ctx the component instance context
@return {Observer} the observer


function initLocalPolyfillPlugins(polyfills, plugins) {
    polyfills.forEach(item => {
        let pluginItem = polyfillPlugin[item.type];
        plugins.push([
            pluginItem, {polyfill: item}
        ]);
    });
}

function initLocalPolyfillPlugins ( polyfills , plugins ) { polyfills . forEach ( item => { let pluginItem = polyfillPlugin [ item . type ] ; plugins . push ( [ pluginItem , { polyfill : item } ] ) ; } ) ; }

Initialize the local polyfill plugins

@inner
@param {Array.<Object>} polyfills the polyfill list
@param {Array} plugins the existed plugin list


function hasBabelDepPlugin(plugins) {
    return plugins.some(item => {
        let pluginItem = item;
        if (Array.isArray(item)) {
            pluginItem = item[0];
        }

        if (typeof pluginItem === 'string') {
            return pluginItem === DEP_PLUGIN_NAME;
        }

        return pluginItem === programPlugins.resolveDep;
    });
}

function hasBabelDepPlugin ( plugins ) { return plugins . some ( item => { let pluginItem = item ; if ( Array . isArray ( item ) ) { pluginItem = item [ 0 ] ; } if ( typeof pluginItem === 'string' ) { return pluginItem === DEP_PLUGIN_NAME ; } return pluginItem === programPlugins . resolveDep ; } ) ; }

Check whether has babel dep resolve plugin in the given plugin list

@inner
@param {Array} plugins the plugin list
@return {boolean}


function normalizeBabelPlugins(plugins, file, buildManager) {
    if (typeof plugins === 'function') {
        plugins = plugins(file);
    }

    plugins = plugins ? [].concat(plugins) : [];
    if (!hasBabelDepPlugin(plugins)) {
        // add npm resolve plugin
        plugins.push(DEP_PLUGIN_NAME);
    }

    return (plugins || []).map(item => {
        if (typeof item === 'string') {
            let result = BUILTIN_PLUGINS[item];
            if (typeof result === 'function') {
                return result(file, buildManager);
            }
        }
        return item;
    });
}

function
normalizeBabelPlugins
(
plugins
,
file
,
buildManager
)
{
if
(
typeof
plugins
===
'function'
)
{
plugins
=
plugins
(
file
)
;
}
plugins
=
plugins
[
]
.
concat
(
plugins
)
:
[
]
;
if
(
!
hasBabelDepPlugin
(
plugins
)
)
{
// add npm resolve plugin
plugins
.
push
(
DEP_PLUGIN_NAME
)
;
}
return
(
plugins
||
[
]
)
.
map
(
item
=>
{
if
(
typeof
item
===
'string'
)
{
let
result
=
BUILTIN_PLUGINS
[
item
]
;
if
(
typeof
result
===
'function'
)
{
return
result
(
file
,
buildManager
)
;
}
}
return
item
;
}
)
;
}

Normalize babel plugins

@inner
@param {Array} plugins the plugins to normalize
@param {Object} file the file to process
@param {BuildManager} buildManager the build manager
@return {Array}


function initBabelProcessorOptions(file, processorOpts, buildManager) {
    processorOpts = Object.assign(
        {}, buildManager.babelConfig, processorOpts
    );

    // init plugins
    let plugins = normalizeBabelPlugins(processorOpts.plugins, file, buildManager);

    // init app/page/component transform plugin
    let configInitHandler = initConfigInfo.bind(
        null, buildManager, 'config', file
    );
    let appBaseClass = buildManager.getOutputAppBaseClass();
    let pluginOpts = {
        appType: buildManager.appType,
        config: configInitHandler
    };
    let filterOptions = buildManager.getFilterTransformOptions();
    let enableMixinSupport = buildManager.isEnableMixinSupport();
    let {api, framework, localPolyfill, polyfill} = buildManager.buildConf;
    let getInitOptions = buildManager.getAppBaseClassInitOptions.bind(
        buildManager, file
    );
    if (file.isEntryScript) {
        Object.assign(pluginOpts, {
            framework,
            registerApi: api,
            baseClass: appBaseClass && appBaseClass.app
        });
        // polyfill using local variable is prior to global polyfill
        localPolyfill || (pluginOpts.polyfill = polyfill);
        pluginOpts.getInitOptions = getInitOptions;
        plugins.push([
            programPlugins.app,
            pluginOpts
        ]);
    }
    else if (file.isPageScript) {
        Object.assign(pluginOpts, {
            enableMixinSupport,
            filterOptions,
            tplRefs: file.tplRefs,
            baseClass: appBaseClass && appBaseClass.page,
            getInitOptions
        });
        plugins.push([programPlugins.page, pluginOpts]);
    }
    else if (file.isComponentScript) {
        Object.assign(pluginOpts, {
            enableMixinSupport,
            filterOptions,
            tplRefs: file.tplRefs,
            baseClass: appBaseClass && appBaseClass.component,
            getInitOptions
        });
        plugins.push([programPlugins.component, pluginOpts]);
    }
    else if (file.isBehavior) {
        plugins.push([programPlugins.behavior, pluginOpts]);
    }

    // init local polyfill plugins
    if (localPolyfill && !file.compiled) {
        initLocalPolyfillPlugins(localPolyfill, plugins);
    }

    processorOpts.plugins = plugins;

    return processorOpts;
}

Initialize babel processor options

@param {Object} file the file to process
@param {Object} processorOpts the processor options
@param {BuildManager} buildManager the build manager
@return {Object}


function startBuild(buildConf, clear) {
    // init build manager
    let buildManager = BuildManager.create(buildConf.appType, buildConf);

    if (clear) {
        buildManager.clear();
    }

    startDevServer(buildConf, buildManager);
    let doneHandler = function () {
        startFileChangeMonitor(buildConf, buildManager);
    };

    // run build
    return runBuild(buildConf, buildManager).then(
        doneHandler,
        doneHandler
    );
}

function
startBuild
(
buildConf
,
clear
)
{
// init build manager
let
buildManager
=
BuildManager
.
create
(
buildConf
.
appType
,
buildConf
)
;
if
(
clear
)
{
buildManager
.
clear
(
)
;
}
startDevServer
(
buildConf
,
buildManager
)
;
let
doneHandler
=
function
(
)
{
startFileChangeMonitor
(
buildConf
,
buildManager
)
;
}
;
// run build
return
runBuild
(
buildConf
,
buildManager
)
.
then
(
doneHandler
,
doneHandler
)
;
}

Start to build small application code to native small application code.

@param {Object} buildConf the config to build
@param {boolean} clear whether clear the old build output before start build
@return {Promise}

Start to build small application code to native small application code .


function parseEventName(name, element, tplOpts, opts) {
    let eventAttrName = name.replace(EVENT_REGEXP, '');
    let [eventType, ...eventModifiers] = eventAttrName.split('.');
    let eventMode = 'on';

    eventType = transformMiniProgramEventType(element, eventType, opts);

    let {logger, file} = tplOpts;

    antNotSupportModifier.forEach(item => {
        if (eventModifiers.includes(item)) {
            logger.warn(
                `${file.path} template event attribute ${name} using`,
                `${item} is not supported in ant env`
            );
        }
    });

    if (eventModifiers.includes('capture')) {
        eventMode = 'catch';
    }

    let nativeEvent = NATIVE_EVENT_MAP[eventType.toLowerCase()];
    if (nativeEvent) {
        eventType = nativeEvent;
    }

    let formatEventType = eventType.charAt(0).toUpperCase() + eventType.substr(1);
    eventAttrName = eventMode + formatEventType;

    return {
        eventType,
        eventAttrName,
        eventModifiers
    };
}

function parseEventName ( name , element , tplOpts , opts ) { let eventAttrName = name . replace ( EVENT_REGEXP , '' ) ; let [ eventType , ... eventModifiers ] = eventAttrName . split ( '.' ) ; let eventMode = 'on' ; eventType = transformMiniProgramEventType ( element , eventType , opts ) ; let { logger , file } = tplOpts ; antNotSupportModifier . forEach ( item => { if ( eventModifiers . includes ( item ) ) { logger . warn ( ` ${ file . path } ${ name } ` , ` ${ item } ` ) ; } } ) ; if ( eventModifiers . includes ( 'capture' ) ) { eventMode = 'catch' ; } let nativeEvent = NATIVE_EVENT_MAP [ eventType . toLowerCase ( ) ] ; if ( nativeEvent ) { eventType = nativeEvent ; } let formatEventType = eventType . charAt ( 0 ) . toUpperCase ( ) + eventType . substr ( 1 ) ; eventAttrName = eventMode + formatEventType ; return { eventType , eventAttrName , eventModifiers } ; }

Parse event bind information

@inner
@param {string} name  event bind attribute name
@param {Object} element the element to transform
@param {Object} tplOpts template parse options
@param {Object} opts the plugin options
@return {Object}


function initExtensions(type, instance, base) {
    let cache = pluginCache;
    if (process.env.APP_TYPE === 'quick') {
        if (!appGlobal.okamPluginCache) {
            appGlobal.okamPluginCache = pluginCache;
        }
        cache = appGlobal.okamPluginCache;
    }

    let existedBase = cache.baseClasses[type];
    if (!existedBase) {
        let plugins = cache.usedExtensions[type];
        let args = [{}];
        plugins && Array.prototype.push.apply(args, plugins);
        args.push(base);

        existedBase = mixin.apply(this, args);
        cache.baseClasses[type] = existedBase;
    }

    return mixin.apply(this, [instance, existedBase]);
}

function initExtensions ( type , instance , base ) { let cache = pluginCache ; if ( process . env . APP_TYPE === 'quick' ) { if ( ! appGlobal . okamPluginCache ) { appGlobal . okamPluginCache = pluginCache ; } cache = appGlobal . okamPluginCache ; } let existedBase = cache . baseClasses [ type ] ; if ( ! existedBase ) { let plugins = cache . usedExtensions [ type ] ; let args = [ { } ] ; plugins && Array . prototype . push . apply ( args , plugins ) ; args . push ( base ) ; existedBase = mixin . apply ( this , args ) ; cache . baseClasses [ type ] = existedBase ; } return mixin . apply ( this , [ instance , existedBase ] ) ; }

Initialize the extensions

@inner
@param {string} type the type to initialize
@param {Object} instance the instance to init
@param {Object} base the instance base to inherit
@return {Object}


function initComponentData(instance, options, isPage) {
    let data = instance.data;
    if (isFunction(data)) {
        instance.data = instance.data();
    }

    instance.$init && instance.$init(isPage, options);
}

function initComponentData ( instance , options , isPage ) { let data = instance . data ; if ( isFunction ( data ) ) { instance . data = instance . data ( ) ; } instance . $init && instance . $init ( isPage , options ) ; }

Initialize the component data

@inner
@param {Object} instance the instance to init
@param {?Object} options the component init extra options information
@param {boolean} isPage whether is page component


function queryRefInstance(selector) {
    let isSelectAll = Array.isArray(selector);
    isSelectAll && (selector = selector[0]);

    let result;
    if (isSelectAll) {
        if (typeof this.selectAllComponents === 'function') {
            result = this.selectAllComponents(selector);
        }
        result || (result = []);
    }
    else if (typeof this.selectComponent === 'function') {
        result = this.selectComponent(selector);
    }

    return result;
}

function queryRefInstance ( selector ) { let isSelectAll = Array . isArray ( selector ) ; isSelectAll && ( selector = selector [ 0 ] ) ; let result ; if ( isSelectAll ) { if ( typeof this . selectAllComponents === 'function' ) { result = this . selectAllComponents ( selector ) ; } result || ( result = [ ] ) ; } else if ( typeof this . selectComponent === 'function' ) { result = this . selectComponent ( selector ) ; } return result ; }

Query the reference instance information by the given reference class

@inner
@param {string|Array.<string>} selector the reference class or id selector
@return {?Object|Array}


function fetchData(url, options) {
    let {method = 'GET'} = options || {};
    method = method.toUpperCase();

    return httpApi.request(Object.assign({url}, options, {method}));
}

function fetchData ( url , options ) { let { method = 'GET' } = options || { } ; method = method . toUpperCase ( ) ; return httpApi . request ( Object . assign ( { url } , options , { method } ) ) ; }

Fetch data by the given url and options

@param {string} url the url to fetch
@param {Object=} options the fetch options
@return {Promise}


function normalizeTransformers(transformerMap) {
    let result = [];
    transformerMap && Object.keys(transformerMap).forEach(k => {
        let item = transformerMap[k];
        if (item) {
            item.transform.type = k;
            result.push(
                Object.assign({}, item, {name: k})
            );
        }
    });
    return result;
}

function normalizeTransformers ( transformerMap ) { let result = [ ] ; transformerMap && Object . keys ( transformerMap ) . forEach ( k => { let item = transformerMap [ k ] ; if ( item ) { item . transform . type = k ; result . push ( Object . assign ( { } , item , { name : k } ) ) ; } } ) ; return result ; }

Normalize the transformers

@param {Object} transformerMap the transform map
@return {Array}


function transform(transformers, element, tplOpts, options) {
    let {config} = tplOpts;
    let onTag = config.onTag;
    if (onTag) {
        onTag(element.name);
    }

    // transform element first
    let transformer = findMatchElemTransformer(transformers.element, element);
    transformer && transformer.call(this, element, tplOpts, options);

    if (element.removed) {
        return;
    }

    // transform element attributes
    let {attribs: attrs} = element;
    attrs && Object.keys(attrs).forEach(k => {
        transformer = findMatchAttrTransformer(transformers.attribute, k);
        if (transformer) {
            transformer.call(this, attrs, k, tplOpts, options, element);
            if (transformer.type === 'for') {
                element.hasForLoop = true;
            }
        }
    });
}

function
transform
(
transformers
,
element
,
tplOpts
,
options
)
{
let
{
config
}
=
tplOpts
;
let
onTag
=
config
.
onTag
;
if
(
onTag
)
{
onTag
(
element
.
name
)
;
}
// transform element first
let
transformer
=
findMatchElemTransformer
(
transformers
.
element
,
element
)
;
transformer
&&
transformer
.
call
(
this
,
element
,
tplOpts
,
options
)
;
if
(
element
.
removed
)
{
return
;
}
// transform element attributes
let
{
attribs
:
attrs
}
=
element
;
attrs
&&
Object
.
keys
(
attrs
)
.
forEach
(
k
=>
{
transformer
=
findMatchAttrTransformer
(
transformers
.
attribute
,
k
)
;
if
(
transformer
)
{
transformer
.
call
(
this
,
attrs
,
k
,
tplOpts
,
options
,
element
)
;
if
(
transformer
.
type
===
'for'
)
{
element
.
hasForLoop
=
true
;
}
}
}
)
;
}

Transform the given template element

@param {Object} transformers the custom transformers
@param {Object} element the element to be visited currently
@param {Object} tplOpts the template transform options
@param {Object} options the template transform plugin options


function transformTextNode(transformer, textNode, tplOpts, options) {
    let handler = findMatchTransformer(transformer, textNode.data, textNode);
    handler && handler.call(this, textNode, tplOpts, options);
}

function transformTextNode ( transformer , textNode , tplOpts , options ) { let handler = findMatchTransformer ( transformer , textNode . data , textNode ) ; handler && handler . call ( this , textNode , tplOpts , options ) ; }

Transform text node

@inner
@param {Object} transformer the text node transformers
@param {Object} textNode the text node to process
@param {Object} tplOpts the template transform options
@param {Object} options the template transform plugin options


function analyseNativeComponent(scriptFile, options) {
    // check whether component is analysed, if true, ignore
    if (scriptFile.isAnalysedComponents) {
        return;
    }
    scriptFile.isAnalysedComponents = true;

    let {appType} = options;
    if (appType === 'quick') {
        return;
    }

    // add native component definition files
    addComponentSameNameFiles(scriptFile, options);
}

Analysis native component

@inner
@param {Object} scriptFile the component script file
@param {Object} options the process options


function compile(file, options) {
    const {resolve: resolveDep, logger} = options;

    try {
        let componentConf = JSON.parse(file.content.toString());
        let usingComponents = componentConf[USING_COMPONENT_KEY];
        if (!usingComponents || !file.component) {
            return {content: file.content};
        }

        let result = {};
        let scriptFile = file.component;
        Object.keys(usingComponents).forEach(k => {
            let value = usingComponents[k];
            if (!value) {
                return;
            }

            let resolvePath = resolveDep(
                scriptFile, value
            );
            result[toHyphen(k)] = resolvePath || value;

            let resolveModInfo = scriptFile.resolvedModIds[value];
            let componentFile = resolveModInfo && resolveModInfo.file;
            componentFile && analyseNativeComponent(componentFile, options);
        });

        // update usingComponent information
        componentConf[USING_COMPONENT_KEY] = result;

        return {
            content: JSON.stringify(componentConf, null, 4)
        };
    }
    catch (ex) {
        logger.error(
            `Parse component config ${file.path} fail`,
             ex.stack || ex.toString()
        );
    }

    return {content: file.content};
}

function
compile
(
file
,
options
)
{
const
{
resolve
:
resolveDep
,
logger
}
=
options
;
try
{
let
componentConf
=
JSON
.
parse
(
file
.
content
.
toString
(
)
)
;
let
usingComponents
=
componentConf
[
USING_COMPONENT_KEY
]
;
if
(
!
usingComponents
||
!
file
.
component
)
{
return
{
content
:
file
.
content
}
;
}
let
result
=
{
}
;
let
scriptFile
=
file
.
component
;
Object
.
keys
(
usingComponents
)
.
forEach
(
k
=>
{
let
value
=
usingComponents
[
k
]
;
if
(
!
value
)
{
return
;
}
let
resolvePath
=
resolveDep
(
scriptFile
,
value
)
;
result
[
toHyphen
(
k
)
]
=
resolvePath
||
value
;
let
resolveModInfo
=
scriptFile
.
resolvedModIds
[
value
]
;
let
componentFile
=
resolveModInfo
&&
resolveModInfo
.
file
;
componentFile
&&
analyseNativeComponent
(
componentFile
,
options
)
;
}
)
;
// update usingComponent information
componentConf
[
USING_COMPONENT_KEY
]
=
result
;
return
{
content
:
JSON
.
stringify
(
componentConf
,
,
4
)
}
;
}
catch
(
ex
)
{
logger
.
error
(
`
${
file
.
path
}
`
,
ex
.
stack
||
ex
.
toString
(
)
)
;
}
return
{
content
:
file
.
content
}
;
}

Process the component dependence sub components based on `usingComponent`
info defined in json.

@param {Object} file the file to process
@param {Object} options the compile options
@return {{content: string}}

Process the component dependence sub components based on usingComponent info defined in json .


function updateArrayItem(observer, idx, value) {
    observer.set(idx, value);
    this[idx] = value;
}

function updateArrayItem ( observer , idx , value ) { observer . set ( idx , value ) ; this [ idx ] = value ; }

Update array item value

@param {Observer} observer the observer
@param {number} idx the index to update
@param {*} value the value to set


function makeArrayObservable(arr, observer, proxyArrApis) {
    // Here, not using __proto__ implementation, there are two import reasons:
    // First, considering __proto__ will be deprecated and is not recommended to use
    // Second, some plugins like weixin contact plugin will change array definition,
    // the array instance __proto__ property does not contains any like `push`
    // `pop` API, and these API only existed in the instance context.
    // So, like the code as the following will not work correctly,
    // a = []; // a.__proto__.push is not defined, a.push is defined
    // a.__proto__.push = function () {};
    // a.push(2); // always call the native push, not the override push method
    // Therefor, using __proto__ to proxy the array method will not work

    Object.keys(proxyArrApis).forEach(method => {
        let rawMethod = arr[method];
        arr[method] = proxyArrApis[method].bind(arr, observer, rawMethod);
    });

    arr.setItem = updateArrayItem.bind(arr, observer);
    arr.getItem = getArrayItem.bind(arr, observer);

    return arr;
}

Make array observable

@param {Array} arr the array to observe
@param {Observer} observer the observer
@param {Object} proxyArrApis the array api to proxy
@return {Array}


function watchDataChange(expressOrFunc, callback, options) {
    if (typeof expressOrFunc === 'function') {
        expressOrFunc = this.__addComputedProp(expressOrFunc);
    }

    if (typeof callback === 'function') {
        let handlerName = getAnonymousWatcherName(this);
        this[handlerName] = callback;
        callback = handlerName;
    }

    return this.__watchDataChange(expressOrFunc, callback, options);
}

function watchDataChange ( expressOrFunc , callback , options ) { if ( typeof expressOrFunc === 'function' ) { expressOrFunc = this . __addComputedProp ( expressOrFunc ) ; } if ( typeof callback === 'function' ) { let handlerName = getAnonymousWatcherName ( this ) ; this [ handlerName ] = callback ; callback = handlerName ; } return this . __watchDataChange ( expressOrFunc , callback , options ) ; }

Watch the given expression or function

@param {string|Function} expressOrFunc the expression or function to watch
@param {string|Function} callback the callback handler to execute when the
expression or function value changes
@param {Object=} options watch options
@param {boolean=} options.immediate whether trigger the callback
immediately with the current value of the expression or function
optional, by default false
@param {boolean=} optional.deep whether watch object nested value
optional, by default false
@return {*}


function initJsProcessor(opts, defaultBabelProcessorName) {
    let plugins = (opts && opts.plugins) || [jsPlugin];
    registerProcessor({
        name: (opts && opts.processor) || defaultBabelProcessorName, // override existed processor
        hook: {
            before(file, options) {
                if (file.isWxCompScript) {
                    options.plugins || (options.plugins = []);
                    options.plugins.push.apply(options.plugins, plugins);
                }
            }
        }
    });
}

function
initJsProcessor
(
opts
,
defaultBabelProcessorName
)
{
let
plugins
=
(
opts
&&
opts
.
plugins
)
||
[
jsPlugin
]
;
registerProcessor
(
{
name
:
(
opts
&&
opts
.
processor
)
||
defaultBabelProcessorName
,
// override existed processor
hook
:
{
before
(
file
,
options
)
{
if
(
file
.
isWxCompScript
)
{
options
.
plugins
||
(
options
.
plugins
=
[
]
)
;
options
.
plugins
.
push
.
apply
(
options
.
plugins
,
plugins
)
;
}
}
}
}
)
;
}

Initialize wx component js processor

@inner
@param {Object=} opts the options to init
@param {string=} opts.processor the builtin processor name, by default `babel`
@param {Array=} opts.plugins the processor plugins
@param {string} defaultBabelProcessorName default babel processor name


function initTplProcessor(opts) {
    registerProcessor({
        name: 'wxml2swan',
        // using the existed view processor
        processor: 'view',
        extnames: ['wxml'],
        rext: 'swan',
        options: opts || {
            plugins: [
                wxmlPlugin
            ]
        }
    });
}

function
initTplProcessor
(
opts
)
{
registerProcessor
(
{
name
:
'wxml2swan'
,
// using the existed view processor
processor
:
'view'
,
extnames
:
[
'wxml'
]
,
rext
:
'swan'
,
options
:
opts
||
{
plugins
:
[
wxmlPlugin
]
}
}
)
;
}

Initialize wx tpl processor

@inner
@param {Object=} opts the processor options
@param {Array=} opts.plugins the view processor plugins


function initStyleProcessor(opts) {
    registerProcessor({
        name: 'wxss2css',
        // using the existed postcss processor
        processor: 'postcss',
        extnames: ['wxss'],
        rext: 'css',
        options: opts || {
            plugins: [
                wxssPlugin
            ]
        }
    });
}

function
initStyleProcessor
(
opts
)
{
registerProcessor
(
{
name
:
'wxss2css'
,
// using the existed postcss processor
processor
:
'postcss'
,
extnames
:
[
'wxss'
]
,
rext
:
'css'
,
options
:
opts
||
{
plugins
:
[
wxssPlugin
]
}
}
)
;
}

Initialize wx style processor

@inner
@param {Object=} opts the processor init options
@param {Array=} opts.plugins the postcss plugins


function initWxsProcessor(opts, defaultBabelProcessorName) {
    registerProcessor({
        name: 'wxs2filter',
        processor(file, options) {
            let content = file.content.toString();

            return {
                content: wxs2filter(content)
            };
        },
        extnames: ['wxs'],
        rext: 'filter.js'
    });
}

function initWxsProcessor ( opts , defaultBabelProcessorName ) { registerProcessor ( { name : 'wxs2filter' , processor ( file , options ) { let content = file . content . toString ( ) ; return { content : wxs2filter ( content ) } ; } , extnames : [ 'wxs' ] , rext : 'filter.js' } ) ; }

Initialize wxs js processor

@inner
@param {Object=} opts the options to init
@param {string=} opts.processor the builtin processor name, by default `babel`
@param {Array=} opts.plugins the processor plugins
@param {string} defaultBabelProcessorName default babel processor name


function initProcessor(options = {}, defaultBabelProcessorName) {
    let {js, css, tpl, wxs} = options;

    if (tpl !== false) {
        initTplProcessor(tpl);
    }

    if (css !== false) {
        initStyleProcessor(css);
    }

    if (js !== false) {
        initJsProcessor(js, defaultBabelProcessorName);
    }

    if (wxs !== false) {
        initWxsProcessor(js, defaultBabelProcessorName);
    }
}

function initProcessor ( options = { } , defaultBabelProcessorName ) { let { js , css , tpl , wxs } = options ; if ( tpl !== false ) { initTplProcessor ( tpl ) ; } if ( css !== false ) { initStyleProcessor ( css ) ; } if ( js !== false ) { initJsProcessor ( js , defaultBabelProcessorName ) ; } if ( wxs !== false ) { initWxsProcessor ( js , defaultBabelProcessorName ) ; } }

Init wx2swan processors

@param {Object=} options the initialization options
@param {Object=} options.js the wx component js processor init options
@param {Object=} options.css the wx component style processor init options
@param {Object=} options.tpl the wx template processor init options
@param {string} defaultBabelProcessorName default babel processor name


function process(file, options) {
    let content = file.content.toString();
    let {config: rules, logger} = options;

    let result = content;
    try {
        result = doReplacement(content, rules || [], file);
    }
    catch (ex) {
        let tip;
        if (ex === 'devServer') {
            tip = ', please execute `npm run dev:server` script or start with `--server` option';
        }
        logger.error('unknown replacement variable:', ex + (tip || ''));
    }

    return {
        content: result
    };
}

Replacement the file content using the given pattern

@param {Object} file the file to process
@param {Object} options the compile options
@return {{content: string}}


function convertMediaQueryToJSExpression(tokens, allAppTypes, appType) {
    let result = [];
    tokens.forEach(item => {
        item = item.trim();
        if (allAppTypes.includes(item)) {
            result.push(`('${item}' === '${appType}')`);
        }
        else if (item === 'and') {
            result.push('&&');
        }
        else if (item === ',' || item === 'or') {
            result.push('||');
        }
        else if (item === 'not') {
            result.push('!');
        }
        else if (item === '(' || item === ')') {
            result.push(item);
        }
    });

    let lastItem = result[result.length - 1];
    if (lastItem === '&&' || lastItem === '||') {
        result.pop();
    }

    // remove no use leading brace if has
    let braceStack = [];
    result.forEach((item, index) => {
        if (item === '(') {
            braceStack.push(index);
        }
        else if (item === ')') {
            braceStack.pop();
        }
    });
    for (let i = braceStack.length - 1; i >= 0; i--) {
        result.splice(braceStack[i], 1);
    }

    return result.join('');
}

function
convertMediaQueryToJSExpression
(
tokens
,
allAppTypes
,
appType
)
{
let
result
=
[
]
;
tokens
.
forEach
(
item
=>
{
item
=
item
.
trim
(
)
;
if
(
allAppTypes
.
includes
(
item
)
)
{
result
.
push
(
`
${
item
}
${
appType
}
`
)
;
}
else
if
(
item
===
'and'
)
{
result
.
push
(
'&&'
)
;
}
else
if
(
item
===
','
||
item
===
'or'
)
{
result
.
push
(
'||'
)
;
}
else
if
(
item
===
'not'
)
{
result
.
push
(
'!'
)
;
}
else
if
(
item
===
'('
||
item
===
')'
)
{
result
.
push
(
item
)
;
}
}
)
;
let
lastItem
=
result
[
result
.
length
1
]
;
if
(
lastItem
===
'&&'
||
lastItem
===
'||'
)
{
result
.
pop
(
)
;
}
// remove no use leading brace if has
let
braceStack
=
[
]
;
result
.
forEach
(
(
item
,
index
)
=>
{
if
(
item
===
'('
)
{
braceStack
.
push
(
index
)
;
}
else
if
(
item
===
')'
)
{
braceStack
.
pop
(
)
;
}
}
)
;
for
(
let
i
=
braceStack
.
length
1
;
i
>=
0
;
i
)
{
result
.
splice
(
braceStack
[
i
]
,
1
)
;
}
return
result
.
join
(
''
)
;
}

Convert media query to js expression

@inner
@param {Array.<string>} tokens the env media query param tokens
@param {Array.<string>} allAppTypes all supported media target types
@param {string} appType current build media target app type
@return {string}


function isAppMediaMatch(params, tokens, allAppTypes, appType) {
    let expression = convertMediaQueryToJSExpression(tokens, allAppTypes, appType);
    try {
        return {
            /* eslint-disable fecs-no-eval */
            value: eval(expression),
            expression
        };
    }
    catch (ex) {
        throw new Error('illegal style env media rule:' + params);
    }
}

function
isAppMediaMatch
(
params
,
tokens
,
allAppTypes
,
appType
)
{
let
expression
=
convertMediaQueryToJSExpression
(
tokens
,
allAppTypes
,
appType
)
;
try
{
return
{
/* eslint-disable fecs-no-eval */
value
:
eval
(
expression
)
,
expression
}
;
}
catch
(
ex
)
{
throw
new
Error
(
'illegal style env media rule:'
+
params
)
;
}
}

Determine the given media app query params is match with the current
build media target.

@inner
@param {string} params the full media query params
@param {Array.<string>} tokens the env media query param tokens
@param {Array.<string>} allAppTypes all supported build media target
@param {string} appType the current build media target
@return {{value: boolean, expression: string}}

Determine the given media app query params is match with the current build media target .


function normalizeAppMediaQueryTokens(tokens) {
    for (let i = tokens.length - 1; i >= 0; i--) {
        let item = tokens[i].trim();
        if (item && item !== '(') {
            break;
        }

        tokens.pop();
    }
}

function normalizeAppMediaQueryTokens ( tokens ) { for ( let i = tokens . length - 1 ; i >= 0 ; i -- ) { let item = tokens [ i ] . trim ( ) ; if ( item && item !== '(' ) { break ; } tokens . pop ( ) ; } }

Normalize the app media query tokens in place.

@inner
@param {Array.<string>} tokens the tokens to normalize

Normalize the app media query tokens in place .


function initAppMediaTargetInfo(ctx, currToken) {
    let {allAppTypes, hasAppMediaType} = ctx;
    let isAppMediaTarget = allAppTypes.includes(currToken);
    hasAppMediaType || (ctx.hasAppMediaType = isAppMediaTarget);

    if (!isAppMediaTarget && !MEDIA_QUERY_OPERATORS.includes(currToken)) {
        return true;
    }

    return false;
}

function initAppMediaTargetInfo ( ctx , currToken ) { let { allAppTypes , hasAppMediaType } = ctx ; let isAppMediaTarget = allAppTypes . includes ( currToken ) ; hasAppMediaType || ( ctx . hasAppMediaType = isAppMediaTarget ) ; if ( ! isAppMediaTarget && ! MEDIA_QUERY_OPERATORS . includes ( currToken ) ) { return true ; } return false ; }

Initialize the app media query target information.
Return whether should stop media query params token parsing.

@inner
@param {Object} ctx the token parser context
@param {string} currToken the current encountered token
@return {boolean}

Initialize the app media query target information . Return whether should stop media query params token parsing .


function initAppMediaQueryToken(ctx, buffer, separator) {
    let currToken = buffer.trim();
    let {tokens} = ctx;
    if (currToken) {
        if (initAppMediaTargetInfo(ctx, currToken)) {
            return true;
        }

        tokens.push(buffer);
        tokens.push(separator);

        return '';
    }

    buffer += separator;
    if (buffer.trim()) {
        tokens.push(buffer);
        return '';
    }
    return buffer;
}

function initAppMediaQueryToken ( ctx , buffer , separator ) { let currToken = buffer . trim ( ) ; let { tokens } = ctx ; if ( currToken ) { if ( initAppMediaTargetInfo ( ctx , currToken ) ) { return true ; } tokens . push ( buffer ) ; tokens . push ( separator ) ; return '' ; } buffer += separator ; if ( buffer . trim ( ) ) { tokens . push ( buffer ) ; return '' ; } return buffer ; }

Initialize the app media query tokens.
Return the new cache token string or true, if return true, meaning that
it should stop parse tokens.

@param {Object} ctx the parse context
@param {string} buffer the current collect token string
@param {string} separator the current separator that is encountered
@return {boolean|string}

Initialize the app media query tokens . Return the new cache token string or true if return true meaning that it should stop parse tokens .


function parseAppMediaQueryParam(allAppTypes, params) {
    let ctx = {
        allAppTypes,
        hasAppMediaType: false,
        tokens: []
    };

    let buffer = '';
    for (let i = 0, len = params.length; i < len; i++) {
        let c = params[i];
        if (QUERY_PARAMS_SEPARATORS.includes(c)) {
            buffer = initAppMediaQueryToken(ctx, buffer, c);
            if (buffer === true) {
                buffer = '';
                break;
            }
        }
        else {
            buffer += c;
        }
    }

    if (buffer) {
        let result = initAppMediaTargetInfo(ctx, buffer.trim());
        result || (ctx.tokens.push(buffer));
    }

    return ctx;
}

function parseAppMediaQueryParam ( allAppTypes , params ) { let ctx = { allAppTypes , hasAppMediaType : false , tokens : [ ] } ; let buffer = '' ; for ( let i = 0 , len = params . length ; i < len ; i ++ ) { let c = params [ i ] ; if ( QUERY_PARAMS_SEPARATORS . includes ( c ) ) { buffer = initAppMediaQueryToken ( ctx , buffer , c ) ; if ( buffer === true ) { buffer = '' ; break ; } } else { buffer += c ; } } if ( buffer ) { let result = initAppMediaTargetInfo ( ctx , buffer . trim ( ) ) ; result || ( ctx . tokens . push ( buffer ) ) ; } return ctx ; }

Parse app media query params info

@inner
@param {Array.<string>} allAppTypes all supported media target types
@param {string} params the full media query params
@return {{hasAppMediaType: boolean, tokens: Array.<string>}}


function compilePug(file, options) {
    let content = file.content.toString();
    // 取出用于pug模板的配置项，包括渲染所需的数据（data字段）
    let config = options.config || {};
    // 考虑到给之后的处理器传递数据，所以这里强制 pretty 为true
    config.pretty = true;
    let data = config.data;
    delete config.data;

    let fn = pug.compile(content, config);
    content = fn(data);

    return {
        content
    };
}

function
compilePug
(
file
,
options
)
{
let
content
=
file
.
content
.
toString
(
)
;
// 取出用于pug模板的配置项，包括渲染所需的数据（data字段）
let
config
=
options
.
config
||
{
}
;
// 考虑到给之后的处理器传递数据，所以这里强制 pretty 为true
config
.
pretty
=
true
;
let
data
=
config
.
data
;
delete
config
.
data
;
let
fn
=
pug
.
compile
(
content
,
config
)
;
content
=
fn
(
data
)
;
return
{
content
}
;
}

Compile pug template

@param {Object} file the file to compile
@param {Object} options for compile pug template
@return {Object}


function initJsProcessor(opts, defaultBabelProcessorName) {
    let plugins = (opts && opts.plugins) || [adapterPlugin];
    registerProcessor({
        name: (opts && opts.processor) || defaultBabelProcessorName, // override existed processor
        hook: {
            before(file, options) {
                if (file.isAntCompScript && !adapterPlugin.isAdapterModule(file.path)) {
                    options.plugins || (options.plugins = []);
                    options.plugins.push.apply(options.plugins, plugins);
                }
            }
        }
    });
}

function
initJsProcessor
(
opts
,
defaultBabelProcessorName
)
{
let
plugins
=
(
opts
&&
opts
.
plugins
)
||
[
adapterPlugin
]
;
registerProcessor
(
{
name
:
(
opts
&&
opts
.
processor
)
||
defaultBabelProcessorName
,
// override existed processor
hook
:
{
before
(
file
,
options
)
{
if
(
file
.
isAntCompScript
&&
!
adapterPlugin
.
isAdapterModule
(
file
.
path
)
)
{
options
.
plugins
||
(
options
.
plugins
=
[
]
)
;
options
.
plugins
.
push
.
apply
(
options
.
plugins
,
plugins
)
;
}
}
}
}
)
;
}

Initialize native component js processor

@inner
@param {Object=} opts the options to init
@param {string=} opts.processor the builtin processor name, by default `babel`
@param {Array=} opts.plugins the processor plugins
@param {string} defaultBabelProcessorName default babel processor name


function initProcessor(options = {}, defaultBabelProcessorName) {
    let {js} = options;

    if (js !== false) {
        initJsProcessor(js, defaultBabelProcessorName);
    }
}

function initProcessor ( options = { } , defaultBabelProcessorName ) { let { js } = options ; if ( js !== false ) { initJsProcessor ( js , defaultBabelProcessorName ) ; } }

Init native transformation processors

@param {Object=} options the initialization options
@param {Object=} options.js the component js processor init options
@param {string} defaultBabelProcessorName default babel processor name


function confirm(params) {
    params = _.extend({
        text: 'Are you sure?',
        yesText: 'Yes',
        yesClass: 'btn-danger',
        noText: 'Cancel',
        escapedHtml: false,
        msgConfirmation: false,
        additionalText: '',
        name: ''
    }, params);
    $('#g-dialog-container').html(ConfirmDialogTemplate({
        params: params
    })).girderModal(false).one('hidden.bs.modal', function () {
        $('#g-confirm-button').off('click');
    });

    const el = $('#g-dialog-container').find('.modal-body>p:first-child');
    if (params.escapedHtml) {
        el.html(params.text);
    } else {
        el.text(params.text);
    }
    if (params['msgConfirmation']) {
        if (params.escapedHtml) {
            $('.g-additional-text').html(params.additionalText);
        } else {
            $('.g-additional-text').text(params.additionalText);
        }
    }

    $('#g-confirm-button').off('click').click(function () {
        if (params['msgConfirmation']) {
            const key = `${params.yesText.toUpperCase()} ${params.name}`;
            const msg = $('#g-confirm-text').val();
            if (msg.toUpperCase() === key.toUpperCase()) {
                $('#g-dialog-container').modal('hide');
                params.confirmCallback();
            } else if (msg.toUpperCase() === '') {
                $('.g-msg-error').html(`Error: You need to enter <b>'${key}'</b>.`);
                $('.g-msg-error').css('color', 'red');
            } else {
                $('.g-msg-error').html(`Error: <b>'${msg}'</b> isn't <b>'${key}'</b>`);
                $('.g-msg-error').css('color', 'red');
            }
        } else {
            $('#g-dialog-container').modal('hide');
            params.confirmCallback();
        }
    });
}

function
confirm
(
params
)
{
params
=
_
.
extend
(
{
text
:
'Are you sure?'
,
yesText
:
'Yes'
,
yesClass
:
'btn-danger'
,
noText
:
'Cancel'
,
escapedHtml
:
false
,
msgConfirmation
:
false
,
additionalText
:
''
,
name
:
''
}
,
params
)
;
$
(
'#g-dialog-container'
)
.
html
(
ConfirmDialogTemplate
(
{
params
:
params
}
)
)
.
girderModal
(
false
)
.
one
(
'hidden.bs.modal'
,
function
(
)
{
$
(
'#g-confirm-button'
)
.
off
(
'click'
)
;
}
)
;
const
el
=
$
(
'#g-dialog-container'
)
.
find
(
'.modal-body>p:first-child'
)
;
if
(
params
.
escapedHtml
)
{
el
.
html
(
params
.
text
)
;
}
else
{
el
.
text
(
params
.
text
)
;
}
if
(
params
[
'msgConfirmation'
]
)
{
if
(
params
.
escapedHtml
)
{
$
(
'.g-additional-text'
)
.
html
(
params
.
additionalText
)
;
}
else
{
$
(
'.g-additional-text'
)
.
text
(
params
.
additionalText
)
;
}
}
$
(
'#g-confirm-button'
)
.
off
(
'click'
)
.
click
(
function
(
)
{
if
(
params
[
'msgConfirmation'
]
)
{
const
key
=
`
${
params
.
yesText
.
toUpperCase
(
)
}
${
params
.
name
}
`
;
const
msg
=
$
(
'#g-confirm-text'
)
.
val
(
)
;
if
(
msg
.
toUpperCase
(
)
===
key
.
toUpperCase
(
)
)
{
$
(
'#g-dialog-container'
)
.
modal
(
'hide'
)
;
params
.
confirmCallback
(
)
;
}
else
if
(
msg
.
toUpperCase
(
)
===
''
)
{
$
(
'.g-msg-error'
)
.
html
(
`
${
key
}
`
)
;
$
(
'.g-msg-error'
)
.
css
(
'color'
,
'red'
)
;
}
else
{
$
(
'.g-msg-error'
)
.
html
(
`
${
msg
}
${
key
}
`
)
;
$
(
'.g-msg-error'
)
.
css
(
'color'
,
'red'
)
;
}
}
else
{
$
(
'#g-dialog-container'
)
.
modal
(
'hide'
)
;
params
.
confirmCallback
(
)
;
}
}
)
;
}

Prompt the user to confirm an action.
@param {Object} [params] Parameters controlling this function's behavior.
@param {String} [params.text] The text to prompt the user with.
@param {String} [params.yesText] The text for the confirm button.
@param {String} [params.yesClass] Class string to apply to the confirm button.
@param {String} [params.noText] The text for the no/cancel button.
@param {Boolean} [params.escapedHtml] If you want to render the text as HTML rather than
plain text, set this to true to acknowledge that you have escaped any
user-created data within the text to prevent XSS exploits.
@param {Boolean} [params.msgConfirmation] If you want to add a new security before
perform an action. This will ask to enter a specific string "params.yesText params.name"
@param {String} [params.additionalText] Additional text to display before the confirmation
input.
@param {String} [params.name] The name to enter in order to confirm an action.
@param {Function} [params.confirmCallback]Callback function when the user confirms the action.


function wrap(obj, funcName, wrapper) {
    obj.prototype[funcName] = _.wrap(obj.prototype[funcName], wrapper);
}

function wrap ( obj , funcName , wrapper ) { obj . prototype [ funcName ] = _ . wrap ( obj . prototype [ funcName ] , wrapper ) ; }

Wrap the prototype method of a given object.

@param obj The object whose prototype to extend, e.g. MyView
@param funcName The name of the function to wrap, e.g. "render"
@param wrapper The wrapper function, which should be a function taking the
underlying wrapped function as its first argument. The wrapped function
should be called with .call(this[, arguments]) inside of the wrapper in
order to preserve "this" semantics.

Wrap the prototype method of a given object .


function (opts) {
    opts = opts || {};
    const defaults = {
        // the default 'method' is 'GET', as set by 'jquery.ajax'

        girderToken: getCurrentToken() || cookie.find('girderToken'),

        error: (error, status) => {
            let info;
            if (error.status === 401) {
                events.trigger('g:loginUi');
                info = {
                    text: 'You must log in to view this resource',
                    type: 'warning',
                    timeout: 4000,
                    icon: 'info'
                };
            } else if (error.status === 403) {
                info = {
                    text: 'Access denied. See the console for more details.',
                    type: 'danger',
                    timeout: 5000,
                    icon: 'attention'
                };
            } else if (error.status === 0 && error.statusText === 'abort') {
                /* We expected this abort, so do nothing. */
                return;
            } else if (error.status === 500 && error.responseJSON &&
                error.responseJSON.type === 'girder') {
                info = {
                    text: error.responseJSON.message,
                    type: 'warning',
                    timeout: 5000,
                    icon: 'info'
                };
            } else if (status === 'parsererror') {
                info = {
                    text: 'A parser error occurred while communicating with the ' +
                          'server (did you use the correct value for `dataType`?). ' +
                          'Details have been logged in the console.',
                    type: 'danger',
                    timeout: 5000,
                    icon: 'attention'
                };
            } else {
                info = {
                    text: 'An error occurred while communicating with the ' +
                          'server. Details have been logged in the console.',
                    type: 'danger',
                    timeout: 5000,
                    icon: 'attention'
                };
            }
            events.trigger('g:alert', info);
            console.error(error.status + ' ' + error.statusText, error.responseText);
        }
    };

    // Overwrite defaults with passed opts, but do not mutate opts
    const args = _.extend({}, defaults, opts);

    if (!args.url) {
        throw new Error('restRequest requires a "url" argument');
    }
    args.url = `${getApiRoot()}${args.url.substring(0, 1) === '/' ? '' : '/'}${args.url}`;

    if (args.girderToken) {
        args.headers = args.headers || {};
        args.headers['Girder-Token'] = args.girderToken;
        delete args.girderToken;
    }

    return Backbone.$.ajax(args);
}

Make a request to the REST API.

This is a wrapper around {@link http://api.jquery.com/jQuery.ajax/ $.ajax}, which also handles
authentication and routing to the Girder API, and provides a default for error notification.

Most users of this method should attach a "done" handler to the return value. In cases where the
server is ordinarily expected to return a non-200 status (e.g. validating user input), the
"error: null" argument should probably be provided, and errors should be processed via an
attached "fail" handler.

Before this function is called, the API root must be set (which typically happens automatically).

@param {Object} opts Options for the request, most of which will be passed through to $.ajax.
@param {string} opts.url The resource path, relative to the API root, without leading or trailing
slashes. e.g. "user/login"
@param {string} [opts.method='GET'] The HTTP method to invoke.
@param {string|Object} [opts.data] The query string or form parameter object.
@param {?Function} [opts.error] An error callback, as documented in
{@link http://api.jquery.com/jQuery.ajax/ $.ajax}, or null. If not provided, this will
have a default behavior of triggering a 'g:alert' global event, with details of the
error, and logging the error to the console. It is recommended that you do not ever pass
a non-null callback function, and handle errors via promise rejection handlers instead.
@param {string} [opts.girderToken] An alternative auth token to use for this request.
@returns {$.Promise} A jqXHR promise, which resolves and rejects
{@link http://api.jquery.com/jQuery.ajax/#jqXHR as documented by $.ajax}.


function sortOperations(op1, op2) {
        var pathCmp = op1.path.localeCompare(op2.path);
        if (pathCmp !== 0) {
            return pathCmp;
        }
        var index1 = methodOrder.indexOf(op1.method);
        var index2 = methodOrder.indexOf(op2.method);
        if (index1 > -1 && index2 > -1) {
            return index1 > index2 ? 1 : (index1 < index2 ? -1 : 0);
        }
        if (index1 > -1) {
            return -1;
        }
        if (index2 > -1) {
            return 1;
        }
        return op1.method.localeCompare(op2.method);
    }

function sortOperations ( op1 , op2 ) { var pathCmp = op1 . path . localeCompare ( op2 . path ) ; if ( pathCmp !== 0 ) { return pathCmp ; } var index1 = methodOrder . indexOf ( op1 . method ) ; var index2 = methodOrder . indexOf ( op2 . method ) ; if ( index1 > - 1 && index2 > - 1 ) { return index1 > index2 ? 1 : ( index1 < index2 ? - 1 : 0 ) ; } if ( index1 > - 1 ) { return - 1 ; } if ( index2 > - 1 ) { return 1 ; } return op1 . method . localeCompare ( op2 . method ) ; }

Comparator to sort operations by path and method. Methods not in the pre-defined ordered list are placed at the end and sorted alphabetically.

Comparator to sort operations by path and method . Methods not in the pre - defined ordered list are placed at the end and sorted alphabetically .


function formatDate(datestr, resolution) {
    datestr = datestr.replace(' ', 'T'); // Cross-browser accepted date format
    var date = new Date(datestr);
    var output = MONTHS[date.getMonth()];

    resolution = resolution || DATE_MONTH;

    if (resolution >= DATE_DAY) {
        output += ' ' + date.getDate() + ',';
    }

    output += ' ' + date.getFullYear();

    if (resolution >= DATE_MINUTE) {
        output += ' at ' + date.getHours() + ':' +
            ('0' + date.getMinutes()).slice(-2);
    }
    if (resolution >= DATE_SECOND) {
        output += ':' + ('0' + date.getSeconds()).slice(-2);
    }

    return output;
}

function
formatDate
(
datestr
,
resolution
)
{
datestr
=
datestr
.
replace
(
' '
,
'T'
)
;
// Cross-browser accepted date format
var
date
=
new
Date
(
datestr
)
;
var
output
=
MONTHS
[
date
.
getMonth
(
)
]
;
resolution
=
resolution
||
DATE_MONTH
;
if
(
resolution
>=
DATE_DAY
)
{
output
+=
' '
+
date
.
getDate
(
)
+
','
;
}
output
+=
' '
+
date
.
getFullYear
(
)
;
if
(
resolution
>=
DATE_MINUTE
)
{
output
+=
' at '
+
date
.
getHours
(
)
+
':'
+
(
'0'
+
date
.
getMinutes
(
)
)
.
slice
(
2
)
;
}
if
(
resolution
>=
DATE_SECOND
)
{
output
+=
':'
+
(
'0'
+
date
.
getSeconds
(
)
)
.
slice
(
2
)
;
}
return
output
;
}

Format a date string to the given resolution.
@param datestr The date string to format.
@param resolution The resolution, defaults to 'day'. Minimum is month.

Format a date string to the given resolution .


function formatSize(sizeBytes) {
    if (sizeBytes < 1024) {
        return sizeBytes + ' B';
    }
    var i, sizeVal = sizeBytes, precision = 1;
    for (i = 0; sizeVal >= 1024; i += 1) {
        sizeVal /= 1024;
    }
    // If we are just reporting a low number, no need for decimal places.
    if (sizeVal < 10) {
        precision = 3;
    } else if (sizeVal < 100) {
        precision = 2;
    }
    return sizeVal.toFixed(precision) + ' ' +
        ['B', 'kB', 'MB', 'GB', 'TB'][Math.min(i, 4)];
}

Format a size in bytes into a human-readable string with metric unit
prefixes.

Format a size in bytes into a human - readable string with metric unit prefixes .


function formatCount(n, opts) {
    n = n || 0;
    opts = opts || {};

    var i = 0,
        base = opts.base || 1000,
        sep = opts.sep || '',
        maxLen = opts.maxLen || 3,
        precision = maxLen - 1;

    for (; n > base; i += 1) {
        n /= base;
    }

    if (!i) {
        precision = 0;
    } else if (n > 100) {
        precision -= 2;
    } else if (n > 10) {
        precision -= 1;
    }

    return n.toFixed(Math.max(0, precision)) + sep +
        ['', 'k', 'M', 'G', 'T'][Math.min(i, 4)];
}

function formatCount ( n , opts ) { n = n || 0 ; opts = opts || { } ; var i = 0 , base = opts . base || 1000 , sep = opts . sep || '' , maxLen = opts . maxLen || 3 , precision = maxLen - 1 ; for ( ; n > base ; i += 1 ) { n /= base ; } if ( ! i ) { precision = 0 ; } else if ( n > 100 ) { precision -= 2 ; } else if ( n > 10 ) { precision -= 1 ; } return n . toFixed ( Math . max ( 0 , precision ) ) + sep + [ '' , 'k' , 'M' , 'G' , 'T' ] [ Math . min ( i , 4 ) ] ; }

Like formatSize, but more generic. Returns a human-readable format
of an integer using metric prefixes. The caller is expected to append any
unit string if necessary.

@param {integer} n The number to format.
@param {Object} [opts={}] Formatting options. These include:
- {integer} [maxLen] Max number of digits in the output.
- {integer} [base=1000] Base for the prefixes (usually 1000 or 1024).
- {string} [sep=''] Separator between numeric value and metric prefix.

Like formatSize but more generic . Returns a human - readable format of an integer using metric prefixes . The caller is expected to append any unit string if necessary .


function localeComparator(model1, model2) {
    var a1 = model1.get(this.sortField),
        a2 = model2.get(this.sortField);

    if (a1 !== undefined && a1.localeCompare) {
        var result = a1.localeCompare(a2) * this.sortDir;
        if (result || !this.secondarySortField) {
            return result;
        }
        a1 = model1.get(this.secondarySortField);
        a2 = model2.get(this.secondarySortField);
        return a1.localeCompare(a2) * this.sortDir;
    }

    return a1 > a2 ? this.sortDir : (a1 < a2 ? -this.sortDir : 0);
}

function localeComparator ( model1 , model2 ) { var a1 = model1 . get ( this . sortField ) , a2 = model2 . get ( this . sortField ) ; if ( a1 !== undefined && a1 . localeCompare ) { var result = a1 . localeCompare ( a2 ) * this . sortDir ; if ( result || ! this . secondarySortField ) { return result ; } a1 = model1 . get ( this . secondarySortField ) ; a2 = model2 . get ( this . secondarySortField ) ; return a1 . localeCompare ( a2 ) * this . sortDir ; } return a1 > a2 ? this . sortDir : ( a1 < a2 ? - this . sortDir : 0 ) ; }

This comparator can be used by collections that wish to support locale-based
sorting.  The locale specifies how upper and lower case are compared.

This comparator can be used by collections that wish to support locale - based sorting . The locale specifies how upper and lower case are compared .


function localeSort(a1, a2) {
    if (a1 !== undefined && a1.localeCompare) {
        return a1.localeCompare(a2);
    }
    return a1 > a2 ? 1 : (a1 < a2 ? -1 : 0);
}

function localeSort ( a1 , a2 ) { if ( a1 !== undefined && a1 . localeCompare ) { return a1 . localeCompare ( a2 ) ; } return a1 > a2 ? 1 : ( a1 < a2 ? - 1 : 0 ) ; }

This comparator can be passed to the sort function on javascript arrays.

This comparator can be passed to the sort function on javascript arrays .


function addFile(filename) {
    var file = zip.addFile(filename);
    var sha = new SHAWriteStream(manifest, filename, file);
    return sha;
  }

function addFile ( filename ) { var file = zip . addFile ( filename ) ; var sha = new SHAWriteStream ( manifest , filename , file ) ; return sha ; }

Add file to zip and it's SHA to manifest

Add file to zip and it s SHA to manifest


function signManifest(template, manifest, callback) {
  var identifier = template.passTypeIdentifier().replace(/^pass./, "");

  var args = [
    "smime",
    "-sign", "-binary",
    "-signer",    Path.resolve(template.keysPath, identifier + ".pem"),
    "-certfile",  Path.resolve(template.keysPath, "wwdr.pem"),
    "-passin",    "pass:" + template.password
  ];
  var sign = execFile("openssl", args, { stdio: "pipe" }, function(error, stdout, stderr) {
    var trimmedStderr = stderr.trim();
    // Windows outputs some unhelpful error messages, but still produces a valid signature
    if (error || (trimmedStderr && trimmedStderr.indexOf('- done') < 0)) {
      callback(new Error(stderr));
    } else {
      var signature = stdout.split(/(\r\n|\n\n)/)[3];
      callback(null, Buffer.from(signature, "base64"));
    }
  });
  sign.stdin.write(manifest);
  sign.stdin.end();
}

Signs a manifest and returns the signature.

Signs a manifest and returns the signature .


function cloneObject(object) {
  var clone = {};
  if (object) {
    for (var key in object)
      clone[key] = object[key];
  }
  return clone;
}

function cloneObject ( object ) { var clone = { } ; if ( object ) { for ( var key in object ) clone [ key ] = object [ key ] ; } return clone ; }

Clone an object by copying all its properties and returning new object. If the argument is missing or null, returns a new object.

Clone an object by copying all its properties and returning new object . If the argument is missing or null returns a new object .


function nanoRemainder(msFloat) {
        var modulo = 1e6;
        var remainder = (msFloat * 1e6) % modulo;
        var positiveRemainder = remainder < 0 ? remainder + modulo : remainder;

        return Math.floor(positiveRemainder);
    }

function nanoRemainder ( msFloat ) { var modulo = 1e6 ; var remainder = ( msFloat * 1e6 ) % modulo ; var positiveRemainder = remainder < 0 ? remainder + modulo : remainder ; return Math . floor ( positiveRemainder ) ; }

Get the decimal part of the millisecond value as nanoseconds

@param {Number} msFloat the number of milliseconds
@returns {Number} an integer number of nanoseconds in the range [0,1e6)

Example: nanoRemainer(123.456789) -> 456789


function getEpoch(epoch) {
        if (!epoch) { return 0; }
        if (typeof epoch.getTime === "function") { return epoch.getTime(); }
        if (typeof epoch === "number") { return epoch; }
        throw new TypeError("now should be milliseconds since UNIX epoch");
    }

Used to grok the `now` parameter to createClock.
@param epoch {Date|number} the system time

Used to grok the now parameter to createClock .


async function tryShutdown () {
    if (jobsToComplete === 0) {
      await new Promise((resolve) => { setTimeout(resolve, 500) })
      await worker.end()
      process.exit()
    }
  }

async function tryShutdown ( ) { if ( jobsToComplete === 0 ) { await new Promise ( ( resolve ) => { setTimeout ( resolve , 500 ) } ) await worker . end ( ) process . exit ( ) } }


function (itemData) {

		var newItems = this.items.concat(itemData),
			// Calculate aspect ratios for items only; exclude spacing
			rowWidthWithoutSpacing = this.width - (newItems.length - 1) * this.spacing,
			newAspectRatio = newItems.reduce(function (sum, item) {
				return sum + item.aspectRatio;
			}, 0),
			targetAspectRatio = rowWidthWithoutSpacing / this.targetRowHeight,
			previousRowWidthWithoutSpacing,
			previousAspectRatio,
			previousTargetAspectRatio;

		// Handle big full-width breakout photos if we're doing them
		if (this.isBreakoutRow) {
			// Only do it if there's no other items in this row
			if (this.items.length === 0) {
				// Only go full width if this photo is a square or landscape
				if (itemData.aspectRatio >= 1) {
					// Close out the row with a full width photo
					this.items.push(itemData);
					this.completeLayout(rowWidthWithoutSpacing / itemData.aspectRatio, 'justify');
					return true;
				}
			}
		}

		if (newAspectRatio < this.minAspectRatio) {

			// New aspect ratio is too narrow / scaled row height is too tall.
			// Accept this item and leave row open for more items.

			this.items.push(merge(itemData));
			return true;

		} else if (newAspectRatio > this.maxAspectRatio) {

			// New aspect ratio is too wide / scaled row height will be too short.
			// Accept item if the resulting aspect ratio is closer to target than it would be without the item.
			// NOTE: Any row that falls into this block will require cropping/padding on individual items.

			if (this.items.length === 0) {

				// When there are no existing items, force acceptance of the new item and complete the layout.
				// This is the pano special case.
				this.items.push(merge(itemData));
				this.completeLayout(rowWidthWithoutSpacing / newAspectRatio, 'justify');
				return true;

			}

			// Calculate width/aspect ratio for row before adding new item
			previousRowWidthWithoutSpacing = this.width - (this.items.length - 1) * this.spacing;
			previousAspectRatio = this.items.reduce(function (sum, item) {
				return sum + item.aspectRatio;
			}, 0);
			previousTargetAspectRatio = previousRowWidthWithoutSpacing / this.targetRowHeight;

			if (Math.abs(newAspectRatio - targetAspectRatio) > Math.abs(previousAspectRatio - previousTargetAspectRatio)) {

				// Row with new item is us farther away from target than row without; complete layout and reject item.
				this.completeLayout(previousRowWidthWithoutSpacing / previousAspectRatio, 'justify');
				return false;

			} else {

				// Row with new item is us closer to target than row without;
				// accept the new item and complete the row layout.
				this.items.push(merge(itemData));
				this.completeLayout(rowWidthWithoutSpacing / newAspectRatio, 'justify');
				return true;

			}

		} else {

			// New aspect ratio / scaled row height is within tolerance;
			// accept the new item and complete the row layout.
			this.items.push(merge(itemData));
			this.completeLayout(rowWidthWithoutSpacing / newAspectRatio, 'justify');
			return true;

		}

	}

Attempt to add a single item to the row.
This is the heart of the justified algorithm.
This method is direction-agnostic; it deals only with sizes, not positions.

If the item fits in the row, without pushing row height beyond min/max tolerance,
the item is added and the method returns true.

If the item leaves row height too high, there may be room to scale it down and add another item.
In this case, the item is added and the method returns true, but the row is incomplete.

If the item leaves row height too short, there are too many items to fit within tolerance.
The method will either accept or reject the new item, favoring the resulting row height closest to within tolerance.
If the item is rejected, left/right padding will be required to fit the row height within tolerance;
if the item is accepted, top/bottom cropping will be required to fit the row height within tolerance.

@method addItem
@param itemData {Object} Item layout data, containing item aspect ratio.
@return {Boolean} True if successfully added; false if rejected.

Attempt to add a single item to the row . This is the heart of the justified algorithm . This method is direction - agnostic ; it deals only with sizes not positions .