<commit_before>(function () {
    'use strict';

    angular.module('ariaNg').factory('aria2RpcService', ['$q', 'aria2RpcConstants', 'aria2RpcErrors', 'ariaNgCommonService', 'ariaNgSettingService', 'aria2HttpRpcService', 'aria2WebSocketRpcService', function ($q, aria2RpcConstants, aria2RpcErrors, ariaNgCommonService, ariaNgSettingService, aria2HttpRpcService, aria2WebSocketRpcService) {
        var rpcImplementService = ariaNgSettingService.isUseWebSocket() ? aria2WebSocketRpcService : aria2HttpRpcService;
        var isConnected = false;
        var secret = ariaNgSettingService.getSecret();

        var onFirstSuccessCallbacks = [];
        var onDownloadStartCallbacks = [];
        var onDownloadPauseCallbacks = [];
        var onDownloadStopCallbacks = [];
        var onDownloadCompleteCallbacks = [];
        var onDownloadErrorCallbacks = [];
        var onBtDownloadCompleteCallbacks = [];

        var checkIsSystemMethod = function (methodName) {
            return methodName.indexOf(aria2RpcConstants.rpcSystemServiceName + '.') === 0;
        };

        var getAria2MethodFullName = function (methodName) {
            return aria2RpcConstants.rpcServiceName + '.' + methodName;
        };

        var getAria2EventFullName = function (eventName) {
            return getAria2MethodFullName(eventName);
        };

        var invoke = function (requestContext, returnContextOnly) {
            if (returnContextOnly) {
                return requestContext;
            }

            var uniqueId = ariaNgCommonService.generateUniqueId();

            var requestBody = {
                jsonrpc: aria2RpcConstants.rpcServiceVersion,
                method: requestContext.methodName,
                id: uniqueId,
                params: requestContext.params
            };

            var invokeContext = {
                uniqueId: uniqueId,
                requestBody: requestBody,
                successCallback: requestContext.successCallback,
                errorCallback: requestContext.errorCallback
            };

            return rpcImplementService.request(invokeContext);
        };

        var registerEvent = function (eventName, callbacks) {
            var fullEventName = getAria2EventFullName(eventName);

            rpcImplementService.on(fullEventName, function (context) {
                if (!angular.isArray(callbacks) || callbacks.length < 1) {
                    return;
                }

                for (var i = 0; i < callbacks.length; i++) {
                    var callback = callbacks[i];
                    callback(context);
                }
            });
        };

        var fireFirstSuccessEvent = function () {
            if (!angular.isArray(onFirstSuccessCallbacks) || onFirstSuccessCallbacks.length < 1) {
                return;
            }

            for (var i = 0; i < onFirstSuccessCallbacks.length; i++) {
                var callback = onFirstSuccessCallbacks[i];
                callback();
            }
        };

        var invokeMulti = function (methodFunc, contexts, callback) {
            var promises = [];

            var hasSuccess = false;
            var hasError = false;
            var results = [];

            for (var i = 0; i < contexts.length; i++) {
                contexts[i].callback = function (response) {
                    results.push(response);

                    hasSuccess = hasSuccess | response.success;
                    hasError = hasError | !response.success;
                };

                promises.push(methodFunc(contexts[i]));
            }

            return $q.all(promises).finally(function () {
                if (callback) {
                    callback({
                        hasSuccess: !!hasSuccess,
                        hasError: !!hasError,
                        results: results
                    });
                }
            });
        };

        var processError = function (error) {
            if (!error || !error.message) {
                return false;
            }

            if (aria2RpcErrors[error.message] && aria2RpcErrors[error.message].tipTextKey) {
                ariaNgCommonService.showError(aria2RpcErrors[error.message].tipTextKey);
                return true;
            } else {
                ariaNgCommonService.showError(error.message);
                return true;
            }
        };

        var buildRequestContext = function () {
            var methodName = arguments[0];
            var isSystemMethod = checkIsSystemMethod(methodName);
            var finalParams = [];

            var context = {
                methodName: (!isSystemMethod ? getAria2MethodFullName(methodName) : methodName)
            };

            if (secret && !isSystemMethod) {
                finalParams.push(aria2RpcConstants.rpcTokenPrefix + secret);
            }

            if (arguments.length > 1) {
                var innerContext = arguments[1];

                context.successCallback = function (id, result) {
                    if (!isConnected) {
                        isConnected = true;
                        fireFirstSuccessEvent();
                    }

                    if (innerContext.callback) {
                        innerContext.callback({
                            id: id,
                            success: true,
                            data: result,
                            context: innerContext
                        });
                    }
                };

                context.errorCallback = function (id, error) {
                    var errorProcessed = false;

                    if (!innerContext.silent) {
                        errorProcessed = processError(error);
                    }

                    if (innerContext.callback) {
                        innerContext.callback({
                            id: id,
                            success: false,
                            data: error,
                            errorProcessed: errorProcessed,
                            context: innerContext
                        });
                    }
                };
            }

            if (arguments.length > 2) {
                for (var i = 2; i < arguments.length; i++) {
                    if (arguments[i] !== null && angular.isDefined(arguments[i])) {
                        finalParams.push(arguments[i]);
                    }
                }
            }

            if (finalParams.length > 0) {
                context.params = finalParams;
            }

            return context;
        };

        (function () {
            registerEvent('onDownloadStart', onDownloadStartCallbacks);
            registerEvent('onDownloadPause', onDownloadPauseCallbacks);
            registerEvent('onDownloadStop', onDownloadStopCallbacks);
            registerEvent('onDownloadComplete', onDownloadCompleteCallbacks);
            registerEvent('onDownloadError', onDownloadErrorCallbacks);
            registerEvent('onBtDownloadComplete', onBtDownloadCompleteCallbacks);
        })();

        return {
            getBasicTaskParams: function () {
                return [
                    'gid',
                    'totalLength',
                    'completedLength',
                    'uploadSpeed',
                    'downloadSpeed',
                    'connections',
                    'numSeeders',
                    'seeder',
                    'status',
                    'errorCode'
                ];
            },
            getFullTaskParams: function () {
                var requestParams = this.getBasicTaskParams();

                requestParams.push('files');
                requestParams.push('bittorrent');

                return requestParams;
            },
            addUri: function (context, returnContextOnly) {
                var urls = context.task.urls;
                var options = angular.copy(context.task.options);

                if (context.pauseOnAdded) {
                    options.pause = 'true';
                }

                return invoke(buildRequestContext('addUri', context, urls, options), !!returnContextOnly);
            },
            addUriMulti: function (context) {
                var contexts = [];

                for (var i = 0; i < context.tasks.length; i++) {
                    var task = context.tasks[i];

                    contexts.push({
                        silent: !!context.silent,
                        task: task,
                        pauseOnAdded: context.pauseOnAdded
                    });
                }

                return invokeMulti(this.addUri, contexts, context.callback);
            },
            addTorrent: function (context, returnContextOnly) {
                var content = context.task.content;
                var options = angular.copy(context.task.options);

                if (context.pauseOnAdded) {
                    options.pause = 'true';
                }

                return invoke(buildRequestContext('addTorrent', context, content, [], options), !!returnContextOnly);
            },
            addMetalink: function (context, returnContextOnly) {
                var content = context.task.content;
                var options = angular.copy(context.task.options);

                if (context.pauseOnAdded) {
                    options.pause = 'true';
                }

                return invoke(buildRequestContext('addMetalink', context, content, [], options), !!returnContextOnly);
            },
            remove: function (context, returnContextOnly) {
                return invoke(buildRequestContext('remove', context, context.gid), !!returnContextOnly);
            },
            forceRemove: function (context, returnContextOnly) {
                return invoke(buildRequestContext('forceRemove', context, context.gid), !!returnContextOnly);
            },
            forceRemoveMulti: function (context) {
                var contexts = [];

                for (var i = 0; i < context.gids.length; i++) {
                    contexts.push({
                        silent: !!context.silent,
                        gid: context.gids[i]
                    });
                }

                return invokeMulti(this.forceRemove, contexts, context.callback);
            },
            pause: function (context, returnContextOnly) {
                return invoke(buildRequestContext('pause', context, context.gid), !!returnContextOnly);
            },
            pauseAll: function (context, returnContextOnly) {
                return invoke(buildRequestContext('pauseAll', context), !!returnContextOnly);
            },
            forcePause: function (context, returnContextOnly) {
                return invoke(buildRequestContext('forcePause', context, context.gid), !!returnContextOnly);
            },
            forcePauseMulti: function (context) {
                var contexts = [];

                for (var i = 0; i < context.gids.length; i++) {
                    contexts.push({
                        silent: !!context.silent,
                        gid: context.gids[i]
                    });
                }

                return invokeMulti(this.forcePause, contexts, context.callback);
            },
            forcePauseAll: function (context, returnContextOnly) {
                return invoke(buildRequestContext('forcePauseAll', context), !!returnContextOnly);
            },
            unpause: function (context, returnContextOnly) {
                return invoke(buildRequestContext('unpause', context, context.gid), !!returnContextOnly);
            },
            unpauseMulti: function (context) {
                var contexts = [];

                for (var i = 0; i < context.gids.length; i++) {
                    contexts.push({
                        silent: !!context.silent,
                        gid: context.gids[i]
                    });
                }

                return invokeMulti(this.unpause, contexts, context.callback);
            },
            unpauseAll: function (context, returnContextOnly) {
                return invoke(buildRequestContext('unpauseAll', context), !!returnContextOnly);
            },
            tellStatus: function (context, returnContextOnly) {
                return invoke(buildRequestContext('tellStatus', context, context.gid), !!returnContextOnly);
            },
            getUris: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getUris', context, context.gid), !!returnContextOnly);
            },
            getFiles: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getFiles', context, context.gid), !!returnContextOnly);
            },
            getPeers: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getPeers', context, context.gid), !!returnContextOnly);
            },
            getServers: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getServers', context, context.gid), !!returnContextOnly);
            },
            tellActive: function (context, returnContextOnly) {
                return invoke(buildRequestContext('tellActive', context,
                    angular.isDefined(context.requestParams) ? context.requestParams: null
                ), !!returnContextOnly);
            },
            tellWaiting: function (context, returnContextOnly) {
                return invoke(buildRequestContext('tellWaiting', context,
                    angular.isDefined(context.offset) ? context.offset : 0,
                    angular.isDefined(context.num) ? context.num : 1000,
                    angular.isDefined(context.requestParams) ? context.requestParams : null
                ), !!returnContextOnly);
            },
            tellStopped: function (context, returnContextOnly) {
                return invoke(buildRequestContext('tellStopped', context,
                    angular.isDefined(context.offset) ? context.offset : 0,
                    angular.isDefined(context.num) ? context.num : 1000,
                    angular.isDefined(context.requestParams) ? context.requestParams: null
                ), !!returnContextOnly);
            },
            changePosition: function (context, returnContextOnly) {
                return invoke(buildRequestContext('changePosition', context, context.gid, context.pos, context.how), !!returnContextOnly);
            },
            changeUri: function (context, returnContextOnly) {
                return invoke(buildRequestContext('changeUri', context, context.gid, context.fileIndex, context.delUris, context.addUris), !!returnContextOnly);
            },
            getOption: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getOption', context, context.gid), !!returnContextOnly);
            },
            changeOption: function (context, returnContextOnly) {
                return invoke(buildRequestContext('changeOption', context, context.gid, context.options), !!returnContextOnly);
            },
            getGlobalOption: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getGlobalOption', context), !!returnContextOnly);
            },
            changeGlobalOption: function (context, returnContextOnly) {
                return invoke(buildRequestContext('changeGlobalOption', context, context.options), !!returnContextOnly);
            },
            getGlobalStat: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getGlobalStat', context), !!returnContextOnly);
            },
            purgeDownloadResult: function (context, returnContextOnly) {
                return invoke(buildRequestContext('purgeDownloadResult', context), !!returnContextOnly);
            },
            removeDownloadResult: function (context, returnContextOnly) {
                return invoke(buildRequestContext('removeDownloadResult', context, context.gid), !!returnContextOnly);
            },
            removeDownloadResultMulti: function (context) {
                var contexts = [];

                for (var i = 0; i < context.gids.length; i++) {
                    contexts.push({
                        silent: !!context.silent,
                        gid: context.gids[i]
                    });
                }

                return invokeMulti(this.removeDownloadResult, contexts, context.callback);
            },
            getVersion: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getVersion', context), !!returnContextOnly);
            },
            getSessionInfo: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getSessionInfo', context), !!returnContextOnly);
            },
            shutdown: function (context, returnContextOnly) {
                return invoke(buildRequestContext('shutdown', context), !!returnContextOnly);
            },
            forceShutdown: function (context, returnContextOnly) {
                return invoke(buildRequestContext('forceShutdown', context), !!returnContextOnly);
            },
            saveSession: function (context, returnContextOnly) {
                return invoke(buildRequestContext('saveSession', context), !!returnContextOnly);
            },
            multicall: function (context) {
                return invoke(buildRequestContext('system.multicall', context, context.methods));
            },
            listMethods: function (context) {
                return invoke(buildRequestContext('system.listMethods', context));
            },
            onFirstSuccess: function (context) {
                onFirstSuccessCallbacks.push(context.callback);
            },
            onDownloadStart: function (context) {
                onDownloadStartCallbacks.push(context.callback);
            },
            onDownloadPause: function (context) {
                onDownloadPauseCallbacks.push(context.callback);
            },
            onDownloadStop: function (context) {
                onDownloadStopCallbacks.push(context.callback);
            },
            onDownloadComplete: function (context) {
                onDownloadCompleteCallbacks.push(context.callback);
            },
            onDownloadError: function (context) {
                onDownloadErrorCallbacks.push(context.callback);
            },
            onBtDownloadComplete: function (context) {
                onBtDownloadCompleteCallbacks.push(context.callback);
            }
        };
    }]);
}());
<commit_msg>modify default order (last completed first) in completed/stopped task page<commit_after>(function () {
    'use strict';

    angular.module('ariaNg').factory('aria2RpcService', ['$q', 'aria2RpcConstants', 'aria2RpcErrors', 'ariaNgCommonService', 'ariaNgSettingService', 'aria2HttpRpcService', 'aria2WebSocketRpcService', function ($q, aria2RpcConstants, aria2RpcErrors, ariaNgCommonService, ariaNgSettingService, aria2HttpRpcService, aria2WebSocketRpcService) {
        var rpcImplementService = ariaNgSettingService.isUseWebSocket() ? aria2WebSocketRpcService : aria2HttpRpcService;
        var isConnected = false;
        var secret = ariaNgSettingService.getSecret();

        var onFirstSuccessCallbacks = [];
        var onDownloadStartCallbacks = [];
        var onDownloadPauseCallbacks = [];
        var onDownloadStopCallbacks = [];
        var onDownloadCompleteCallbacks = [];
        var onDownloadErrorCallbacks = [];
        var onBtDownloadCompleteCallbacks = [];

        var checkIsSystemMethod = function (methodName) {
            return methodName.indexOf(aria2RpcConstants.rpcSystemServiceName + '.') === 0;
        };

        var getAria2MethodFullName = function (methodName) {
            return aria2RpcConstants.rpcServiceName + '.' + methodName;
        };

        var getAria2EventFullName = function (eventName) {
            return getAria2MethodFullName(eventName);
        };

        var invoke = function (requestContext, returnContextOnly) {
            if (returnContextOnly) {
                return requestContext;
            }

            var uniqueId = ariaNgCommonService.generateUniqueId();

            var requestBody = {
                jsonrpc: aria2RpcConstants.rpcServiceVersion,
                method: requestContext.methodName,
                id: uniqueId,
                params: requestContext.params
            };

            var invokeContext = {
                uniqueId: uniqueId,
                requestBody: requestBody,
                successCallback: requestContext.successCallback,
                errorCallback: requestContext.errorCallback
            };

            return rpcImplementService.request(invokeContext);
        };

        var registerEvent = function (eventName, callbacks) {
            var fullEventName = getAria2EventFullName(eventName);

            rpcImplementService.on(fullEventName, function (context) {
                if (!angular.isArray(callbacks) || callbacks.length < 1) {
                    return;
                }

                for (var i = 0; i < callbacks.length; i++) {
                    var callback = callbacks[i];
                    callback(context);
                }
            });
        };

        var fireFirstSuccessEvent = function () {
            if (!angular.isArray(onFirstSuccessCallbacks) || onFirstSuccessCallbacks.length < 1) {
                return;
            }

            for (var i = 0; i < onFirstSuccessCallbacks.length; i++) {
                var callback = onFirstSuccessCallbacks[i];
                callback();
            }
        };

        var invokeMulti = function (methodFunc, contexts, callback) {
            var promises = [];

            var hasSuccess = false;
            var hasError = false;
            var results = [];

            for (var i = 0; i < contexts.length; i++) {
                contexts[i].callback = function (response) {
                    results.push(response);

                    hasSuccess = hasSuccess | response.success;
                    hasError = hasError | !response.success;
                };

                promises.push(methodFunc(contexts[i]));
            }

            return $q.all(promises).finally(function () {
                if (callback) {
                    callback({
                        hasSuccess: !!hasSuccess,
                        hasError: !!hasError,
                        results: results
                    });
                }
            });
        };

        var processError = function (error) {
            if (!error || !error.message) {
                return false;
            }

            if (aria2RpcErrors[error.message] && aria2RpcErrors[error.message].tipTextKey) {
                ariaNgCommonService.showError(aria2RpcErrors[error.message].tipTextKey);
                return true;
            } else {
                ariaNgCommonService.showError(error.message);
                return true;
            }
        };

        var buildRequestContext = function () {
            var methodName = arguments[0];
            var isSystemMethod = checkIsSystemMethod(methodName);
            var finalParams = [];

            var context = {
                methodName: (!isSystemMethod ? getAria2MethodFullName(methodName) : methodName)
            };

            if (secret && !isSystemMethod) {
                finalParams.push(aria2RpcConstants.rpcTokenPrefix + secret);
            }

            if (arguments.length > 1) {
                var innerContext = arguments[1];

                context.successCallback = function (id, result) {
                    if (!isConnected) {
                        isConnected = true;
                        fireFirstSuccessEvent();
                    }

                    if (innerContext.callback) {
                        innerContext.callback({
                            id: id,
                            success: true,
                            data: result,
                            context: innerContext
                        });
                    }
                };

                context.errorCallback = function (id, error) {
                    var errorProcessed = false;

                    if (!innerContext.silent) {
                        errorProcessed = processError(error);
                    }

                    if (innerContext.callback) {
                        innerContext.callback({
                            id: id,
                            success: false,
                            data: error,
                            errorProcessed: errorProcessed,
                            context: innerContext
                        });
                    }
                };
            }

            if (arguments.length > 2) {
                for (var i = 2; i < arguments.length; i++) {
                    if (arguments[i] !== null && angular.isDefined(arguments[i])) {
                        finalParams.push(arguments[i]);
                    }
                }
            }

            if (finalParams.length > 0) {
                context.params = finalParams;
            }

            return context;
        };

        (function () {
            registerEvent('onDownloadStart', onDownloadStartCallbacks);
            registerEvent('onDownloadPause', onDownloadPauseCallbacks);
            registerEvent('onDownloadStop', onDownloadStopCallbacks);
            registerEvent('onDownloadComplete', onDownloadCompleteCallbacks);
            registerEvent('onDownloadError', onDownloadErrorCallbacks);
            registerEvent('onBtDownloadComplete', onBtDownloadCompleteCallbacks);
        })();

        return {
            getBasicTaskParams: function () {
                return [
                    'gid',
                    'totalLength',
                    'completedLength',
                    'uploadSpeed',
                    'downloadSpeed',
                    'connections',
                    'numSeeders',
                    'seeder',
                    'status',
                    'errorCode'
                ];
            },
            getFullTaskParams: function () {
                var requestParams = this.getBasicTaskParams();

                requestParams.push('files');
                requestParams.push('bittorrent');

                return requestParams;
            },
            addUri: function (context, returnContextOnly) {
                var urls = context.task.urls;
                var options = angular.copy(context.task.options);

                if (context.pauseOnAdded) {
                    options.pause = 'true';
                }

                return invoke(buildRequestContext('addUri', context, urls, options), !!returnContextOnly);
            },
            addUriMulti: function (context) {
                var contexts = [];

                for (var i = 0; i < context.tasks.length; i++) {
                    var task = context.tasks[i];

                    contexts.push({
                        silent: !!context.silent,
                        task: task,
                        pauseOnAdded: context.pauseOnAdded
                    });
                }

                return invokeMulti(this.addUri, contexts, context.callback);
            },
            addTorrent: function (context, returnContextOnly) {
                var content = context.task.content;
                var options = angular.copy(context.task.options);

                if (context.pauseOnAdded) {
                    options.pause = 'true';
                }

                return invoke(buildRequestContext('addTorrent', context, content, [], options), !!returnContextOnly);
            },
            addMetalink: function (context, returnContextOnly) {
                var content = context.task.content;
                var options = angular.copy(context.task.options);

                if (context.pauseOnAdded) {
                    options.pause = 'true';
                }

                return invoke(buildRequestContext('addMetalink', context, content, [], options), !!returnContextOnly);
            },
            remove: function (context, returnContextOnly) {
                return invoke(buildRequestContext('remove', context, context.gid), !!returnContextOnly);
            },
            forceRemove: function (context, returnContextOnly) {
                return invoke(buildRequestContext('forceRemove', context, context.gid), !!returnContextOnly);
            },
            forceRemoveMulti: function (context) {
                var contexts = [];

                for (var i = 0; i < context.gids.length; i++) {
                    contexts.push({
                        silent: !!context.silent,
                        gid: context.gids[i]
                    });
                }

                return invokeMulti(this.forceRemove, contexts, context.callback);
            },
            pause: function (context, returnContextOnly) {
                return invoke(buildRequestContext('pause', context, context.gid), !!returnContextOnly);
            },
            pauseAll: function (context, returnContextOnly) {
                return invoke(buildRequestContext('pauseAll', context), !!returnContextOnly);
            },
            forcePause: function (context, returnContextOnly) {
                return invoke(buildRequestContext('forcePause', context, context.gid), !!returnContextOnly);
            },
            forcePauseMulti: function (context) {
                var contexts = [];

                for (var i = 0; i < context.gids.length; i++) {
                    contexts.push({
                        silent: !!context.silent,
                        gid: context.gids[i]
                    });
                }

                return invokeMulti(this.forcePause, contexts, context.callback);
            },
            forcePauseAll: function (context, returnContextOnly) {
                return invoke(buildRequestContext('forcePauseAll', context), !!returnContextOnly);
            },
            unpause: function (context, returnContextOnly) {
                return invoke(buildRequestContext('unpause', context, context.gid), !!returnContextOnly);
            },
            unpauseMulti: function (context) {
                var contexts = [];

                for (var i = 0; i < context.gids.length; i++) {
                    contexts.push({
                        silent: !!context.silent,
                        gid: context.gids[i]
                    });
                }

                return invokeMulti(this.unpause, contexts, context.callback);
            },
            unpauseAll: function (context, returnContextOnly) {
                return invoke(buildRequestContext('unpauseAll', context), !!returnContextOnly);
            },
            tellStatus: function (context, returnContextOnly) {
                return invoke(buildRequestContext('tellStatus', context, context.gid), !!returnContextOnly);
            },
            getUris: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getUris', context, context.gid), !!returnContextOnly);
            },
            getFiles: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getFiles', context, context.gid), !!returnContextOnly);
            },
            getPeers: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getPeers', context, context.gid), !!returnContextOnly);
            },
            getServers: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getServers', context, context.gid), !!returnContextOnly);
            },
            tellActive: function (context, returnContextOnly) {
                return invoke(buildRequestContext('tellActive', context,
                    angular.isDefined(context.requestParams) ? context.requestParams: null
                ), !!returnContextOnly);
            },
            tellWaiting: function (context, returnContextOnly) {
                return invoke(buildRequestContext('tellWaiting', context,
                    angular.isDefined(context.offset) ? context.offset : 0,
                    angular.isDefined(context.num) ? context.num : 1000,
                    angular.isDefined(context.requestParams) ? context.requestParams : null
                ), !!returnContextOnly);
            },
            tellStopped: function (context, returnContextOnly) {
                return invoke(buildRequestContext('tellStopped', context,
                    angular.isDefined(context.offset) ? context.offset : -1,
                    angular.isDefined(context.num) ? context.num : 1000,
                    angular.isDefined(context.requestParams) ? context.requestParams: null
                ), !!returnContextOnly);
            },
            changePosition: function (context, returnContextOnly) {
                return invoke(buildRequestContext('changePosition', context, context.gid, context.pos, context.how), !!returnContextOnly);
            },
            changeUri: function (context, returnContextOnly) {
                return invoke(buildRequestContext('changeUri', context, context.gid, context.fileIndex, context.delUris, context.addUris), !!returnContextOnly);
            },
            getOption: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getOption', context, context.gid), !!returnContextOnly);
            },
            changeOption: function (context, returnContextOnly) {
                return invoke(buildRequestContext('changeOption', context, context.gid, context.options), !!returnContextOnly);
            },
            getGlobalOption: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getGlobalOption', context), !!returnContextOnly);
            },
            changeGlobalOption: function (context, returnContextOnly) {
                return invoke(buildRequestContext('changeGlobalOption', context, context.options), !!returnContextOnly);
            },
            getGlobalStat: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getGlobalStat', context), !!returnContextOnly);
            },
            purgeDownloadResult: function (context, returnContextOnly) {
                return invoke(buildRequestContext('purgeDownloadResult', context), !!returnContextOnly);
            },
            removeDownloadResult: function (context, returnContextOnly) {
                return invoke(buildRequestContext('removeDownloadResult', context, context.gid), !!returnContextOnly);
            },
            removeDownloadResultMulti: function (context) {
                var contexts = [];

                for (var i = 0; i < context.gids.length; i++) {
                    contexts.push({
                        silent: !!context.silent,
                        gid: context.gids[i]
                    });
                }

                return invokeMulti(this.removeDownloadResult, contexts, context.callback);
            },
            getVersion: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getVersion', context), !!returnContextOnly);
            },
            getSessionInfo: function (context, returnContextOnly) {
                return invoke(buildRequestContext('getSessionInfo', context), !!returnContextOnly);
            },
            shutdown: function (context, returnContextOnly) {
                return invoke(buildRequestContext('shutdown', context), !!returnContextOnly);
            },
            forceShutdown: function (context, returnContextOnly) {
                return invoke(buildRequestContext('forceShutdown', context), !!returnContextOnly);
            },
            saveSession: function (context, returnContextOnly) {
                return invoke(buildRequestContext('saveSession', context), !!returnContextOnly);
            },
            multicall: function (context) {
                return invoke(buildRequestContext('system.multicall', context, context.methods));
            },
            listMethods: function (context) {
                return invoke(buildRequestContext('system.listMethods', context));
            },
            onFirstSuccess: function (context) {
                onFirstSuccessCallbacks.push(context.callback);
            },
            onDownloadStart: function (context) {
                onDownloadStartCallbacks.push(context.callback);
            },
            onDownloadPause: function (context) {
                onDownloadPauseCallbacks.push(context.callback);
            },
            onDownloadStop: function (context) {
                onDownloadStopCallbacks.push(context.callback);
            },
            onDownloadComplete: function (context) {
                onDownloadCompleteCallbacks.push(context.callback);
            },
            onDownloadError: function (context) {
                onDownloadErrorCallbacks.push(context.callback);
            },
            onBtDownloadComplete: function (context) {
                onBtDownloadCompleteCallbacks.push(context.callback);
            }
        };
    }]);
}());
<|endoftext|>


<commit_before>var _ = require('lodash');
var Authority = require('../authority');

var defaultOptions = {
	assignField: 'auth'
};

function identify(auth, opts) {
	opts = _.defaults(opts || {}, defaultOptions);

	if (!(auth instanceof Authority))
		throw new TypeError;

	return function(req, res, next) {
		if (req[opts.assignField] !== undefined)
			return next();
		getUserAndRole(auth, req, function(err, authInfo) {
			if (err)
				return next(err);
			req[opts.assignField] = authInfo;
			next();
		});
	};
}

function getUserAndRole(auth, req, cb) {
	auth.getUser(req, function(err, user) {
		if (err || !user)
			return cb(err, null);
		user.getRole(function(err, role) {
			if (err || !role)
				return cb(err, null);
			cb(null, { user: user, role: role });
		});
	});
}

module.exports = exports = identify;
exports.defaultOptions = defaultOptions;
<commit_msg>Use duck-typing instead of strong type checking<commit_after>var _ = require('lodash');

var defaultOptions = {
	assignField: 'auth'
};

function validAuthority(authority) {
	return authority.getUser !== undefined;
}

function identify(auth, opts) {
	opts = _.defaults(opts || {}, defaultOptions);

	if (!validAuthority(auth))
		throw new TypeError;

	return function(req, res, next) {
		if (req[opts.assignField] !== undefined)
			return next();
		getUserAndRole(auth, req, function(err, authInfo) {
			if (err)
				return next(err);
			req[opts.assignField] = authInfo;
			next();
		});
	};
}

function getUserAndRole(auth, req, cb) {
	auth.getUser(req, function(err, user) {
		if (err || !user)
			return cb(err, null);
		user.getRole(function(err, role) {
			if (err || !role)
				return cb(err, null);
			cb(null, { user: user, role: role });
		});
	});
}

module.exports = exports = identify;
exports.defaultOptions = defaultOptions;
<|endoftext|>


<commit_before>$(function(){
    var requestUrl = '/picselection/ajax',
        imgBox = null,
        cropHints = [];
    $("#submit").on('click',function(){
        /*count*/
        var images = [],
            pics = document.forms[0].pic,
            sum = 0,
            index = []

	// 筛选索引
        if(!(pics[0] == null || pics[0] == undefined)) {
            for (var i = 0;i < pics.length; ++i){
                if (pics[i].checked){
                    index.push(i);
                    sum++;
                }
            }
            // 获取数据
            var imgTemp;
            for(var i in index){
                var index_i = index[i];
                imgTemp = imgBox[index_i];
                if ((cropHints[index_i].bottom != 0) && (cropHints[index_i].right != 0)){
                    imgTemp.cropHint = cropHints[index_i];
                }
                images.push(JSON.stringify(imgTemp));
            }
        }else{
            if(pics.checked){
                imgBox[0].cropHint = cropHints[0];
                images.push(JSON.stringify(imgBox[0]));
                sum = 1;
            }
        }
        if (sum > 0){
            if(confirm("当前选中了" + sum + "张图片。是否确认提交？")){
                /*get poiId*/
                var poiId = $('.city').attr("data-id"),
                    postData = {
                        poiId: poiId,
                        images : images
                    };
                console.log(postData);
                /*post data*/
                $.ajax({
                    url : requestUrl,
                    type: "post",
                    data: postData,
                    dataType: "json",
                    traditional: true,
                    dataProcess: false,
                    contentType: "application/x-www-form-urlencoded; charset=utf-8",
                    success : function (msg) {
                        if (msg.code == 0){
                            alert('提交成功.');
                            $(this).attr('data-submit','true');
                        }
                        else{
                            alert('Failed to submit!');
                        }
                    },
                    error : function () {
                        alert('Error!!!');
                    }
                })
            }
        }else{
            alert('当前并未选中任何图片，请先选择符合合适的图片！');
        }
    })

    $("#next").on('click',function(){
        if ($('#submit').attr('data-submit') == "true"){
            $.ajax({
                url : requestUrl,
                type: "GET",
                data: {},
                success : function (data) {
                    console.log(data);

                    if (data.code == 0){
                        imgBox = data.images;
                        /*modify the DOM*/
                        $('form').remove();
                        $('.city').children('b').text(data.name);
                        $('.city').attr('data-id' , data.poiId);
                        $('.imgCnt').children('b').text(data.images.length);
                        $('.restCity').children('b').text(data.rest);
                        $('.doneCity').children('b').text(data.done);

                        /*add the DOM*/
                        $('.hd').after('<form></form>');
                        var imgBlock;
                        for(var i in imgBox){
                            imgBlock =
                                '
' +
                                    '' +
                                        '' +
                                    '' +
                                    '<input class="pic" name="pic" type="checkbox" value="' + i + '"/>' +
                                '
';
                            $('form').append(imgBlock);
                            cropHints[i] = {
                                "top": 0,
                                "left": 0,
                                "bottom": 0,
                                "right": 0
                            };
                        }

                        /*add the imgLayer*/
                        $('a.img').on('click', function(){
                            $('.sd_layer').css("display","block");
                            var index = $(this).attr("data-id");
                                img = '';
                            $('.img_layer .img_can').append(img);
                            $('.img_layer').css("display","block");
                            $('#ow').val(imgBox[index].w);
                            $('#oh').val(imgBox[index].h);

                            var selectFlag = false;//if cut ,the flag change to true
                            function showCoords(c){
                                selectFlag = true;
                                $('#x1').val(c.x);
                                $('#y1').val(c.y);
                                $('#x2').val(c.x2);
                                $('#y2').val(c.y2);
                                $('#w').val(c.w);
                                $('#h').val(c.h);
                                $('#cw').val(jcrop_api.getBounds()[0]);
                                $('#ch').val(jcrop_api.getBounds()[1]);
                            };

                            function clearCoords(){
                                $('#x1').val('');
                                $('#y1').val('');
                                $('#x2').val('');
                                $('#y2').val('');
                                $('#w').val('');
                                $('#h').val('');
                            };

                            $('#imageCrop').Jcrop({
                                onChange: showCoords,
                                onSelect: showCoords,
                                onRelease: clearCoords
                            },function(){
                                jcrop_api = this;
                            });

                            $('#coords').on('change','input',function(e){
                                var x1 = $('#x1').val(),
                                    x2 = $('#x2').val(),
                                    y1 = $('#y1').val(),
                                    y2 = $('#y2').val();
                                jcrop_api.setSelect([x1,y1,x2,y2]);
                            });

                            /*close sdLayer*/
                            $('.sd_layer').on('click', function(){
                                $(this).css("display","none");
                                $('.img_layer').css("display","none");
                                $('#coords input').val('');
                                $('.img_layer .img_can').empty();
                            });
                            $('#close').on('click',function(){
                                $('.sd_layer').trigger('click');
                            })

                            $('#imgSubmit').off('click');
                            $('#imgSubmit').on('click',function(){
                                if (!selectFlag){
                                    if(confirm("并未对该图进行裁剪,是否保持当前尺寸?")){
                                        var ow = $('#ow').val(),
                                            oh = $('#oh').val(),
                                            cropHint;
                                        ow = (ow % 2) ? (ow - 1) : ow;
                                        oh = (oh % 2) ? (oh - 1) : oh;
                                        cropHint = {
                                            "top": 0,
                                            "left": 0,
                                            "bottom": oh,
                                            "right": ow
                                        };
                                        cropHints[index] = cropHint;
                                        alert("裁剪成功!");
                                        $('.sd_layer').trigger('click');
                                    }else{

                                    }
                                }else{
                                    var x1 = $('#x1').val(),
                                        x2 = $('#x2').val(),
                                        y1 = $('#y1').val(),
                                        y2 = $('#y2').val(),
                                        cw = $('#cw').val(),
                                        ch = $('#ch').val(),
                                        ow = $('#ow').val(),
                                        oh = $('#oh').val(),
                                        left = x1 * ow / cw,
                                        top = y1 * oh / ch,
                                        right = x2 * ow / cw,
                                        bottom = y2 * oh / ch,
                                        cropHint= {};

                                    left = parseInt(left);
                                    left = (left % 2) ? (left + 1) : left;
                                    right = parseInt(right);
                                    right = (right % 2) ? (right + 1) : right;
                                    top = parseInt(top);
                                    top = (top % 2) ? (top + 1) : top;
                                    bottom = parseInt(bottom);
                                    bottom = (bottom % 2) ? (bottom + 1) : bottom;

                                    cropHint = {
                                        "top": top,
                                        "left": left,
                                        "bottom": bottom,
                                        "right": right
                                    };
                                    cropHints[index] = cropHint;
                                    alert("裁剪成功!");
                                    $('.sd_layer').trigger('click');
                                }
                            });
                        });
                    }else{
                        alert('Failed to get data!');
                    }
                },
                error : function () {
                    alert('Error!!!');
                }
            })
        }else{
            alert('请先提交改动!');
        }
    });
    $(window).load(function(){
        $("#next").trigger('click');
        var wHeight = $(window).height(),
            wWidth = $(window).width(),
            imgBoxHeight = 634,
            imgBoxWidth = 1034,
            top = (wHeight - imgBoxHeight)/2,
            left = (wWidth - imgBoxWidth)/2;
        $('.img_layer').css('top' , top);
        $('.img_layer').css('left' , left);
    });
})
<commit_msg>add to sub<commit_after>$(function(){
    var requestUrl = '/picselection/ajax',
        imgBox = null,
        cropHints = [];
    $("#submit").on('click',function(){
        /*count*/
        var images = [],
            pics = document.forms[0].pic,
            sum = 0,
            index = []

	// 筛选索引
        if(!(pics[0] == null || pics[0] == undefined)) {
            for (var i = 0;i < pics.length; ++i){
                if (pics[i].checked){
                    index.push(i);
                    sum++;
                }
            }
            // 获取数据
            var imgTemp;
            for(var i in index){
                var index_i = index[i];
                imgTemp = imgBox[index_i];
                if ((cropHints[index_i].bottom != 0) && (cropHints[index_i].right != 0)){
                    imgTemp.cropHint = cropHints[index_i];
                }
                images.push(JSON.stringify(imgTemp));
            }
        }else{
            if(pics.checked){
                imgBox[0].cropHint = cropHints[0];
                images.push(JSON.stringify(imgBox[0]));
                sum = 1;
            }
        }
        if (sum > 0){
            if(confirm("当前选中了" + sum + "张图片。是否确认提交？")){
                /*get poiId*/
                var poiId = $('.city').attr("data-id"),
                    postData = {
                        poiId: poiId,
                        images : images
                    };
                console.log(postData);
                /*post data*/
                $.ajax({
                    url : requestUrl,
                    type: "post",
                    data: postData,
                    dataType: "json",
                    traditional: true,
                    dataProcess: false,
                    contentType: "application/x-www-form-urlencoded; charset=utf-8",
                    success : function (msg) {
                        if (msg.code == 0){
                            alert('提交成功.');
                            $(this).attr('data-submit','true');
                        }
                        else{
                            alert('Failed to submit!');
                        }
                    },
                    error : function () {
                        alert('Error!!!');
                    }
                })
            }
        }else{
            alert('当前并未选中任何图片，请先选择符合合适的图片！');
        }
    })

    $("#next").on('click',function(){
        if ($('#submit').attr('data-submit') == "true"){
            $.ajax({
                url : requestUrl,
                type: "GET",
                data: {},
                success : function (data) {
                    console.log(data);

                    if (data.code == 0){
                        imgBox = data.images;
                        /*modify the DOM*/
                        $('form').remove();
                        $('.city').children('b').text(data.name);
                        $('.city').attr('data-id' , data.poiId);
                        $('.imgCnt').children('b').text(data.images.length);
                        $('.restCity').children('b').text(data.rest);
                        $('.doneCity').children('b').text(data.done);

                        /*add the DOM*/
                        $('.hd').after('<form></form>');
                        var imgBlock;
                        for(var i in imgBox){
                            imgBlock =
                                '
' +
                                    '' +
                                        '' +
                                    '' +
                                    '<input class="pic" name="pic" type="checkbox" value="' + i + '"/>' +
                                '
';
                            $('form').append(imgBlock);
                            cropHints[i] = {
                                "top": 0,
                                "left": 0,
                                "bottom": 0,
                                "right": 0
                            };
                        }

                        /*add the imgLayer*/
                        $('a.img').on('click', function(){
                            $('.sd_layer').css("display","block");
                            var index = $(this).attr("data-id");
                                img = '';
                            $('.img_layer .img_can').append(img);
                            $('.img_layer').css("display","block");
                            $('#ow').val(imgBox[index].w);
                            $('#oh').val(imgBox[index].h);

                            var selectFlag = false;//if cut ,the flag change to true
                            function showCoords(c){
                                selectFlag = true;
                                $('#x1').val(c.x);
                                $('#y1').val(c.y);
                                $('#x2').val(c.x2);
                                $('#y2').val(c.y2);
                                $('#w').val(c.w);
                                $('#h').val(c.h);
                                $('#cw').val(jcrop_api.getBounds()[0]);
                                $('#ch').val(jcrop_api.getBounds()[1]);
                            };

                            function clearCoords(){
                                $('#x1').val('');
                                $('#y1').val('');
                                $('#x2').val('');
                                $('#y2').val('');
                                $('#w').val('');
                                $('#h').val('');
                            };

                            $('#imageCrop').Jcrop({
                                onChange: showCoords,
                                onSelect: showCoords,
                                onRelease: clearCoords
                            },function(){
                                jcrop_api = this;
                            });

                            $('#coords').on('change','input',function(e){
                                var x1 = $('#x1').val(),
                                    x2 = $('#x2').val(),
                                    y1 = $('#y1').val(),
                                    y2 = $('#y2').val();
                                jcrop_api.setSelect([x1,y1,x2,y2]);
                            });

                            /*close sdLayer*/
                            $('.sd_layer').on('click', function(){
                                $(this).css("display","none");
                                $('.img_layer').css("display","none");
                                $('#coords input').val('');
                                $('.img_layer .img_can').empty();
                            });
                            $('#close').on('click',function(){
                                $('.sd_layer').trigger('click');
                            })

                            $('#imgSubmit').off('click');
                            $('#imgSubmit').on('click',function(){
                                if (!selectFlag){
                                    if(confirm("并未对该图进行裁剪,是否保持当前尺寸?")){
                                        var ow = $('#ow').val(),
                                            oh = $('#oh').val(),
                                            cropHint;
                                        ow = (ow % 2) ? (ow - 1) : ow;
                                        oh = (oh % 2) ? (oh - 1) : oh;
                                        cropHint = {
                                            "top": 0,
                                            "left": 0,
                                            "bottom": oh,
                                            "right": ow
                                        };
                                        cropHints[index] = cropHint;
                                        alert("裁剪成功!");
                                        $('.sd_layer').trigger('click');
                                    }else{

                                    }
                                }else{
                                    var x1 = $('#x1').val(),
                                        x2 = $('#x2').val(),
                                        y1 = $('#y1').val(),
                                        y2 = $('#y2').val(),
                                        cw = $('#cw').val(),
                                        ch = $('#ch').val(),
                                        ow = $('#ow').val(),
                                        oh = $('#oh').val(),
                                        left = x1 * ow / cw,
                                        top = y1 * oh / ch,
                                        right = x2 * ow / cw,
                                        bottom = y2 * oh / ch,
                                        cropHint= {};

                                    left = parseInt(left);
                                    left = (left % 2) ? (left + 1) : left;
                                    right = parseInt(right);
                                    right = (right % 2) ? (right - 1) : right;
                                    top = parseInt(top);
                                    top = (top % 2) ? (top + 1) : top;
                                    bottom = parseInt(bottom);
                                    bottom = (bottom % 2) ? (bottom - 1) : bottom;

                                    cropHint = {
                                        "top": top,
                                        "left": left,
                                        "bottom": bottom,
                                        "right": right
                                    };
                                    cropHints[index] = cropHint;
                                    alert("裁剪成功!");
                                    $('.sd_layer').trigger('click');
                                }
                            });
                        });
                    }else{
                        alert('Failed to get data!');
                    }
                },
                error : function () {
                    alert('Error!!!');
                }
            })
        }else{
            alert('请先提交改动!');
        }
    });
    $(window).load(function(){
        $("#next").trigger('click');
        var wHeight = $(window).height(),
            wWidth = $(window).width(),
            imgBoxHeight = 634,
            imgBoxWidth = 1034,
            top = (wHeight - imgBoxHeight)/2,
            left = (wWidth - imgBoxWidth)/2;
        $('.img_layer').css('top' , top);
        $('.img_layer').css('left' , left);
    });
})
<|endoftext|>


<commit_before>'use strict';

const {HttpClient, HttpClientResponse} = require('./HttpClient');

/**
 * HTTP client which uses a `fetch` function to issue requests. This fetch
 * function is expected to be the Web Fetch API function or an equivalent, such
 * as the function provided by the node-fetch package (https://github.com/node-fetch/node-fetch).
 */
class FetchHttpClient extends HttpClient {
  constructor(fetchFn) {
    super();
    this._fetchFn = fetchFn;
  }

  /** @override. */
  getClientName() {
    return 'fetch';
  }

  makeRequest(
    host,
    port,
    path,
    method,
    headers,
    requestData,
    protocol,
    timeout
  ) {
    const isInsecureConnection = protocol === 'http';

    const url = new URL(
      path,
      `${isInsecureConnection ? 'http' : 'https'}://${host}`
    );
    url.port = port;

    const fetchPromise = this._fetchFn(url.toString(), {
      method,
      headers,
      body: requestData || undefined,
    });

    // The Fetch API does not support passing in a timeout natively, so a
    // timeout promise is constructed to race against the fetch and preempt the
    // request, simulating a timeout.
    //
    // This timeout behavior differs from Node:
    // - Fetch uses a single timeout for the entire length of the request.
    // - Node is more fine-grained and resets the timeout after each stage of
    //   the request.
    //
    // As an example, if the timeout is set to 30s and the connection takes 20s
    // to be established followed by 20s for the body, Fetch would timeout but
    // Node would not. The more fine-grained timeout cannot be implemented with
    // fetch.
    let pendingTimeoutId;
    const timeoutPromise = new Promise((_, reject) => {
      pendingTimeoutId = setTimeout(() => {
        pendingTimeoutId = null;
        reject(HttpClient.makeTimeoutError());
      }, timeout);
    });

    return Promise.race([fetchPromise, timeoutPromise])
      .then((res) => {
        return new FetchHttpClientResponse(res);
      })
      .finally(() => {
        if (pendingTimeoutId) {
          clearTimeout(pendingTimeoutId);
        }
      });
  }
}

class FetchHttpClientResponse extends HttpClientResponse {
  constructor(res) {
    super(
      res.status,
      FetchHttpClientResponse._transformHeadersToObject(res.headers)
    );
    this._res = res;
  }

  getRawResponse() {
    return this._res;
  }

  toStream(streamCompleteCallback) {
    // Unfortunately `fetch` does not have event handlers for when the stream is
    // completely read. We therefore invoke the streamCompleteCallback right
    // away. This callback emits a response event with metadata and completes
    // metrics, so it's ok to do this without waiting for the stream to be
    // completely read.
    streamCompleteCallback();

    // Fetch's `body` property is expected to be a readable stream of the body.
    return this._res.body;
  }

  toJSON() {
    return this._res.json();
  }

  static _transformHeadersToObject(headers) {
    // Fetch uses a Headers instance so this must be converted to a barebones
    // JS object to meet the HttpClient interface.
    const headersObj = {};

    for (const entry of headers) {
      if (!Array.isArray(entry) || entry.length != 2) {
        throw new Error(
          'Response objects produced by the fetch function given to FetchHttpClient do not have an iterable headers map. Response#headers should be an iterable object.'
        );
      }

      headersObj[entry[0]] = entry[1];
    }

    return headersObj;
  }
}

module.exports = {FetchHttpClient, FetchHttpClientResponse};
<commit_msg>Update FetchHttpClient to make fetch function optional. (#1253)<commit_after>'use strict';

const {HttpClient, HttpClientResponse} = require('./HttpClient');

/**
 * HTTP client which uses a `fetch` function to issue requests.
 *
 * By default relies on the global `fetch` function, but an optional function
 * can be passed in. If passing in a function, it is expected to match the Web
 * Fetch API. As an example, this could be the function provided by the
 * node-fetch package (https://github.com/node-fetch/node-fetch).
 */
class FetchHttpClient extends HttpClient {
  constructor(fetchFn) {
    super();
    this._fetchFn = fetchFn;
  }

  /** @override. */
  getClientName() {
    return 'fetch';
  }

  makeRequest(
    host,
    port,
    path,
    method,
    headers,
    requestData,
    protocol,
    timeout
  ) {
    const isInsecureConnection = protocol === 'http';

    const url = new URL(
      path,
      `${isInsecureConnection ? 'http' : 'https'}://${host}`
    );
    url.port = port;

    const fetchFn = this._fetchFn || fetch;
    const fetchPromise = fetchFn(url.toString(), {
      method,
      headers,
      body: requestData || undefined,
    });

    // The Fetch API does not support passing in a timeout natively, so a
    // timeout promise is constructed to race against the fetch and preempt the
    // request, simulating a timeout.
    //
    // This timeout behavior differs from Node:
    // - Fetch uses a single timeout for the entire length of the request.
    // - Node is more fine-grained and resets the timeout after each stage of
    //   the request.
    //
    // As an example, if the timeout is set to 30s and the connection takes 20s
    // to be established followed by 20s for the body, Fetch would timeout but
    // Node would not. The more fine-grained timeout cannot be implemented with
    // fetch.
    let pendingTimeoutId;
    const timeoutPromise = new Promise((_, reject) => {
      pendingTimeoutId = setTimeout(() => {
        pendingTimeoutId = null;
        reject(HttpClient.makeTimeoutError());
      }, timeout);
    });

    return Promise.race([fetchPromise, timeoutPromise])
      .then((res) => {
        return new FetchHttpClientResponse(res);
      })
      .finally(() => {
        if (pendingTimeoutId) {
          clearTimeout(pendingTimeoutId);
        }
      });
  }
}

class FetchHttpClientResponse extends HttpClientResponse {
  constructor(res) {
    super(
      res.status,
      FetchHttpClientResponse._transformHeadersToObject(res.headers)
    );
    this._res = res;
  }

  getRawResponse() {
    return this._res;
  }

  toStream(streamCompleteCallback) {
    // Unfortunately `fetch` does not have event handlers for when the stream is
    // completely read. We therefore invoke the streamCompleteCallback right
    // away. This callback emits a response event with metadata and completes
    // metrics, so it's ok to do this without waiting for the stream to be
    // completely read.
    streamCompleteCallback();

    // Fetch's `body` property is expected to be a readable stream of the body.
    return this._res.body;
  }

  toJSON() {
    return this._res.json();
  }

  static _transformHeadersToObject(headers) {
    // Fetch uses a Headers instance so this must be converted to a barebones
    // JS object to meet the HttpClient interface.
    const headersObj = {};

    for (const entry of headers) {
      if (!Array.isArray(entry) || entry.length != 2) {
        throw new Error(
          'Response objects produced by the fetch function given to FetchHttpClient do not have an iterable headers map. Response#headers should be an iterable object.'
        );
      }

      headersObj[entry[0]] = entry[1];
    }

    return headersObj;
  }
}

module.exports = {FetchHttpClient, FetchHttpClientResponse};
<|endoftext|>


<commit_before>const { Parser: AcornParser } = require('acorn')
const AcornStage3 = require('acorn-stage3')

const { AbstractParser } = require('./AbstractParser')
const { NameParser } = require('./NameParser')
const { PropParser } = require('./PropParser')
const { DataParser } = require('./DataParser')
const { ComputedParser } = require('./ComputedParser')
const { MethodParser } = require('./MethodParser')
const { EventParser } = require('./EventParser')
const { CommentParser } = require('./CommentParser')

const { Comment } = require('../entity/Comment')
const { DescriptionEntry } = require('../entity/DescriptionEntry')
const { KeywordsEntry } = require('../entity/KeywordsEntry')
const { Syntax, Properties, Features } = require('../Enum')

const acorn = AcornParser.extend(AcornStage3)

class ScriptParser extends AbstractParser {
  /**
   * @param {Parser} parser - The Parser object
   */
  constructor (root) {
    super(root)

    this.comments = []
    this.tokens = []
    this.declarations = {}
    this.eventsEmmited = {}

    const locations = true
    const onComment = (block, text, start, end, loc) => {
      this.comments.push(new Comment(block, text, start, end, loc))
    }

    const onToken = (token) => this.tokens.push(token)
    const options = { ...root.options, locations, onComment, onToken }

    this.source = root.options.source.script.trim()
    this.ast = acorn.parse(this.source, options)
  }

  getTokenNode (token) {
    const locations = true
    const options = { ...this.root.options, locations }

    return acorn.parseExpressionAt(this.source, token.start, options)
  }

  findTokenIndex (node) {
    /* eslint-disable arrow-body-style */
    return this.tokens.findIndex(({ loc }) => {
      return loc.start.line === node.loc.start.line
    })
  }

  findParentToken (node) {
    let index = this.findTokenIndex(node)

    if (index > 0) {
      while (index > -1) {
        const token = this.tokens[index--]

        if (token.type.startsExpr && token.type.beforeExpr) {
          return token
        }
      }
    }

    return undefined
  }

  findPreviousToken (node) {
    const index = this.findTokenIndex(node) - 1

    if (index > -1) {
      return this.tokens[index]
    }

    return undefined
  }

  findComment (node) {
    const token = this.findPreviousToken(node) || { end: -1 }

    /* eslint-disable arrow-body-style */
    return this.comments.reverse().find(({ start, end }) => {
      return end < node.start && start > token.end
    })
  }

  parseComment (node) {
    if (!this.root.features.includes(Features.description)
        && !this.root.features.includes(Features.keywords)) {
      return
    }

    const comment = this.findComment(node)

    if (comment) {
      const { description, keywords } = CommentParser.parse(comment.text)

      if (this.root.features.includes(Features.description)) {
        if (description) {
          this.emit(new DescriptionEntry(description))
        }
      }

      if (this.root.features.includes(Features.keywords)) {
        if (keywords) {
          this.emit(new KeywordsEntry(keywords))
        }
      }
    }
  }

  parse (node = this.ast) {
    node.body.forEach((item) => {
      switch (item.type) {
        case Syntax.VariableDeclaration:
          item.declarations.forEach((declaration) => {
            this.declarations[declaration.id.name] = declaration.init
          })

          this.parseVariableDeclaration(item)
          break

        case Syntax.ExportNamedDeclaration:
        case Syntax.ExportDefaultDeclaration:
          this.parseComment(item)
          this.parseExportDefaultDeclaration(item.declaration)
          break

        case Syntax.ExpressionStatement:
          this.parseExpressionStatement(item)
          break
      }
    })
  }

  parseExpressionStatement (node) {
    switch (node.expression.type) {
      case Syntax.CallExpression:
        this.parseExportDefaultDeclaration(node.expression)
        break

      case Syntax.AssignmentExpression: {
        let enableParsing = false

        switch (node.expression.left.object.name) {
          case 'module':
            if (node.expression.left.property.name === 'exports') {
              enableParsing = true
            }
            break

          case 'exports':
            if (node.expression.left.property.value === 'default') {
              enableParsing = true
            }
            break
        }

        if (enableParsing) {
          this.parseComment(node)
          this.parseExportDefaultDeclaration(node.expression.right)
        }
        break
      }
    }
  }

  parseFunctionExpression (node) {
    this.parseExportDefaultDeclaration(node.body)
  }

  parseIdentifier (node) {
    this.parseExportDefaultDeclaration(node)
  }

  parseExportDefaultDeclaration (node) {
    switch (node.type) {
      case Syntax.ObjectExpression:
        node.properties
          .filter(({ key }) => Properties.hasOwnProperty(key.name))
          .forEach((property) => this.parseFeature(property))
        break

      case Syntax.CallExpression:
        if (node.arguments.length) {
          this.parseExportDefaultDeclaration(node.arguments[0])
        }
        break

      case Syntax.FunctionExpression:
      case Syntax.ArrowFunctionExpression:
        this.parseFunctionExpression(node)
        break

      case Syntax.Identifier: {
        const identifierNode = this.declarations[node.name]

        if (identifierNode) {
          this.parseIdentifier(identifierNode)
        }
        break
      }

      default:
        // Ignore statement
    }
  }

  parseFeature (property) {
    let parser = null

    switch (property.key.name) {
      case Properties.name:
        parser = new NameParser(this)
        break

      case Properties.data:
        parser = new DataParser(this)
        break

      case Properties.props:
        parser = new PropParser(this)
        break

      case Properties.computed:
        parser = new ComputedParser(this)
        break

      case Properties.methods:
        parser = new MethodParser(this, {
          defaultVisibility: this.root.options.defaultMethodVisibility
        })
        break

      default:
        parser = new EventParser(this)
        break
    }

    parser.parse(property.value)
  }
}

module.exports.ScriptParser = ScriptParser
<commit_msg>Add ScriptParser.parseAssignmentExpression()<commit_after>const { Parser: AcornParser } = require('acorn')
const AcornStage3 = require('acorn-stage3')

const { AbstractParser } = require('./AbstractParser')
const { NameParser } = require('./NameParser')
const { PropParser } = require('./PropParser')
const { DataParser } = require('./DataParser')
const { ComputedParser } = require('./ComputedParser')
const { MethodParser } = require('./MethodParser')
const { EventParser } = require('./EventParser')
const { CommentParser } = require('./CommentParser')

const { Comment } = require('../entity/Comment')
const { DescriptionEntry } = require('../entity/DescriptionEntry')
const { KeywordsEntry } = require('../entity/KeywordsEntry')
const { Syntax, Properties, Features } = require('../Enum')

const acorn = AcornParser.extend(AcornStage3)

class ScriptParser extends AbstractParser {
  /**
   * @param {Parser} parser - The Parser object
   */
  constructor (root) {
    super(root)

    this.comments = []
    this.tokens = []
    this.declarations = {}
    this.eventsEmmited = {}

    const locations = true
    const onComment = (block, text, start, end, loc) => {
      this.comments.push(new Comment(block, text, start, end, loc))
    }

    const onToken = (token) => this.tokens.push(token)
    const options = { ...root.options, locations, onComment, onToken }

    this.source = root.options.source.script.trim()
    this.ast = acorn.parse(this.source, options)
  }

  getTokenNode (token) {
    const locations = true
    const options = { ...this.root.options, locations }

    return acorn.parseExpressionAt(this.source, token.start, options)
  }

  findTokenIndex (node) {
    /* eslint-disable arrow-body-style */
    return this.tokens.findIndex(({ loc }) => {
      return loc.start.line === node.loc.start.line
    })
  }

  findParentToken (node) {
    let index = this.findTokenIndex(node)

    if (index > 0) {
      while (index > -1) {
        const token = this.tokens[index--]

        if (token.type.startsExpr && token.type.beforeExpr) {
          return token
        }
      }
    }

    return undefined
  }

  findPreviousToken (node) {
    const index = this.findTokenIndex(node) - 1

    if (index > -1) {
      return this.tokens[index]
    }

    return undefined
  }

  findComment (node) {
    const token = this.findPreviousToken(node) || { end: -1 }

    /* eslint-disable arrow-body-style */
    return this.comments.reverse().find(({ start, end }) => {
      return end < node.start && start > token.end
    })
  }

  parseComment (node) {
    if (!this.root.features.includes(Features.description)
        && !this.root.features.includes(Features.keywords)) {
      return
    }

    const comment = this.findComment(node)

    if (comment) {
      const { description, keywords } = CommentParser.parse(comment.text)

      if (this.root.features.includes(Features.description)) {
        if (description) {
          this.emit(new DescriptionEntry(description))
        }
      }

      if (this.root.features.includes(Features.keywords)) {
        if (keywords) {
          this.emit(new KeywordsEntry(keywords))
        }
      }
    }
  }

  parse (node = this.ast) {
    node.body.forEach((item) => {
      switch (item.type) {
        case Syntax.VariableDeclaration:
          item.declarations.forEach((declaration) => {
            this.declarations[declaration.id.name] = declaration.init
          })

          this.parseVariableDeclaration(item)
          break

        case Syntax.ExportNamedDeclaration:
        case Syntax.ExportDefaultDeclaration:
          this.parseComment(item)
          this.parseExportDefaultDeclaration(item.declaration)
          break

        case Syntax.ExpressionStatement:
          this.parseExpressionStatement(item)
          break
      }
    })
  }

  parseExpressionStatement (node) {
    switch (node.expression.type) {
      case Syntax.CallExpression:
        this.parseExportDefaultDeclaration(node.expression)
        break

      case Syntax.AssignmentExpression:
        this.parseAssignmentExpression(node.expression)
        break
    }
  }

  parseAssignmentExpression (node) {
    let enableParsing = false

    switch (node.left.object.name) {
      case 'module':
        if (node.left.property.name === 'exports') {
          enableParsing = true
        }
        break

      case 'exports':
        if (node.left.property.value === 'default') {
          enableParsing = true
        }
        break
    }

    if (enableParsing) {
      this.parseComment(node)
      this.parseExportDefaultDeclaration(node.right)
    }
  }

  parseFunctionExpression (node) {
    this.parseExportDefaultDeclaration(node.body)
  }

  parseIdentifier (node) {
    this.parseExportDefaultDeclaration(node)
  }

  parseExportDefaultDeclaration (node) {
    switch (node.type) {
      case Syntax.ObjectExpression:
        node.properties
          .filter(({ key }) => Properties.hasOwnProperty(key.name))
          .forEach((property) => this.parseFeature(property))
        break

      case Syntax.CallExpression:
        if (node.arguments.length) {
          this.parseExportDefaultDeclaration(node.arguments[0])
        }
        break

      case Syntax.FunctionExpression:
      case Syntax.ArrowFunctionExpression:
        this.parseFunctionExpression(node)
        break

      case Syntax.Identifier: {
        const identifierNode = this.declarations[node.name]

        if (identifierNode) {
          this.parseIdentifier(identifierNode)
        }
        break
      }

      default:
        // Ignore statement
    }
  }

  parseFeature (property) {
    let parser = null

    switch (property.key.name) {
      case Properties.name:
        parser = new NameParser(this)
        break

      case Properties.data:
        parser = new DataParser(this)
        break

      case Properties.props:
        parser = new PropParser(this)
        break

      case Properties.computed:
        parser = new ComputedParser(this)
        break

      case Properties.methods:
        parser = new MethodParser(this, {
          defaultVisibility: this.root.options.defaultMethodVisibility
        })
        break

      default:
        parser = new EventParser(this)
        break
    }

    parser.parse(property.value)
  }
}

module.exports.ScriptParser = ScriptParser
<|endoftext|>


<commit_before><commit_msg>add 'levelHtml is sanitized' test<commit_after><|endoftext|>


<commit_before>"use strict";

class claraConfigurator {
  constructor(config) {
    this.api                    = null;
    this.claraSceneId           = null;
    this.playerDivId            = null;
    this.configuratorDivId      = null;
    this.configuratorInputDivId = null;
    this.configuratorForm       = null;
    this.addtocartClassName     = null;
    this.addtocartButton        = null;
    this.addtocartEnabled       = false;
    this.variation_idClassName  = null;
    this.variation_idInput      = null;
    /*
    *
    */
    this.attributes           = null;
    /*
    *
    */
    this.available_attributes = null;

    if (config) {
      var self = this;
      // init variables
      this.playerDivId            = config.playerDivId;
      this.configuratorDivId      = config.configuratorDivId;
      this.claraSceneId           = config.claraSceneId;
      this.attributes             = config.attributes;
      this.available_attributes   = config.available_attributes;
      this.configuratorInputDivId = config.configuratorInputDivId;
      this.addtocartClassName     = config.addtocartClassName;
      this.variation_idClassName  = config.variation_idClassName;

      this.addtocartButton = document.getElementsByClassName(this.addtocartClassName)[0];
      this.addtocartButton.onclick = (ev) => {
        if (!self.addtocartEnabled) {
          event.preventDefault();
        }
      };
      this.variation_idInput = document.getElementsByClassName(this.variation_idClassName)[0];
    }
    console.log(this.attributes);
    console.log(this.available_attributes);
    this._initClara();
  }

  _initClara() {
    var self = this;
    // Fetch and initialize the sceneId
    this.api = claraplayer(this.playerDivId);
    this.api.sceneIO
      .fetchAndUse(this.claraSceneId, null, { waitForPublish: true })
      .then(() => {

        // use the first form in the scene
        self._getConfiguratorForm();

        var configuratorEl = document.getElementById(self.configuratorDivId);

        if (self.configuratorForm && configuratorEl) {
          self.api.configuration.initConfigurator({
            id    : self.claraSceneId,
            form  : self.configuratorForm,
            el    : configuratorEl
          });

          self.api.on('configurationChange', (ev) => {
            self._onConfigurationChange();
          });
        }
      });
  }

  _getConfiguratorForm() {
    if (!this.api) {
      return;
    }
    var forms = this.api.configuration.getForms();
    if (forms.length > 0) {
      this.configuratorForm = forms[0].name;
    }
  }

  _onConfigurationChange() {
    var self = this;
    var config = this.api.configuration.getConfiguration();
    console.log(config);

    var configuratorInputDiv = document.getElementById(this.configuratorInputDivId);
    if (!configuratorInputDiv) {
      return;
    }

    // check if config attribute name and value exist for the current product
    var additionalAttrs = [];
    if (Object.keys(this.attributes).length < Object.keys(config).length) {
      console.warn("Threekit attribute number is smaller than in WooCommerce, product will not be able to added to cart");
    }
    for (var key in config) {
      // looking for "key" in attributes
      var found = false;
      var legal = true;
      for (var ele in this.attributes) {
        // remove "pa_"
        var trimEle = ele;
        if (trimEle.startsWith('pa_')) {
          trimEle = trimEle.substr(3);
        }
        if (this.ignoreCaseStrcmp(trimEle, key)) {
          found = true;
          if (!this.ignoreCaseIncludes(this.attributes[ele], config[key])) {
            // config attribute value is illegal
            legal = false;
          }
          break;
        }
      }
      if (!legal) {
        this._disableAddtocartButton();
        return;
      }
      if (!found) {
        // additional attributes will be posted to server as text
        additionalAttrs.push(key);
      }
    }

    /*
    *  Check if the product is available
    *  Attributes in WooCommerce can be overlapping,
    *  using the first matching attribute in this.available_attributes
    */
    var foundMatch = false;
    for (var i = 0; i < this.available_attributes.length; i++) {
      var attrs = this.available_attributes[i].attributes;
      if (Object.keys(attrs).length != Object.keys(config).length) {
        continue;
      }
      var match = true;
      for (var key in config) {
        var found = false;
        for(var ele in attrs) {
          // remove "attribute_pa_"
          var trimEle = ele;
          if (trimEle.startsWith('attribute_pa_')) {
            trimEle = trimEle.substr(13);
          }
          if (this.ignoreCaseStrcmp(trimEle, key)) {
            if (attrs[ele] === "" || this.ignoreCaseStrcmp(attrs[ele], config[key])) {
              found = true;
              break;
            }
          }
        }
        if (!found) {
          // matching fail
          match = false;
          break;
        }
      }
      if (match) {
        // find a match!
        foundMatch = true;
        console.log(this.available_attributes[i].variation_id);
        this._enableAddtocartButton();
        this.variation_idInput.value = this.available_attributes[i].variation_id;
        break;
      }
    }
    if (!foundMatch) {
      this._disableAddtocartButton();
    }

    // calculate price

  }

  _disableAddtocartButton() {
    if (!this.addtocartButton) {
      return;
    }
    this.addtocartEnabled = false;
    this.addtocartButton.classList.add('disabled');
  }

  _enableAddtocartButton() {
    if (!this.addtocartButton) {
      return;
    }
    this.addtocartEnabled = true;
    this.addtocartButton.classList.remove('disabled');
  }

  /*
  * Utility functions
  */
  ignoreCaseStrcmp(str1, str2) {
    return str1.toLowerCase() === str2.toLowerCase();
  }
  ignoreCaseIncludes(arr, ele) {
    var self = this;
    for (var iter in arr) {
      if (self.ignoreCaseStrcmp(arr[iter], ele)) {
        return true;
      }
    }
    return false;
  }
}

(function() {
  var opts = {
    addtocartClassName    : 'single_add_to_cart_button',
    variation_idClassName : 'variation_id',
    playerDivId           : 'clara-player',
    configuratorDivId     : 'panel-embed',
    configuratorInputDivId: 'threekit-add-to-cart-inputs',
    claraSceneId          : php_vars.clarauuid,
    available_attributes  : php_vars.available_attributes,
    attributes            : php_vars.attributes
  };
  var cc = new claraConfigurator(opts);

}());
<commit_msg>submit other inputs<commit_after>"use strict";

class claraConfigurator {
  constructor(config) {
    this.api                    = null;
    this.claraSceneId           = null;
    this.playerDivId            = null;
    this.configuratorDivId      = null;
    this.configuratorInputDivId = null;
    this.configuratorForm       = null;
    this.addtocartClassName     = null;
    this.addtocartButton        = null;
    this.addtocartEnabled       = false;
    this.variation_idClassName  = null;
    this.variation_idInput      = null;
    this.variationInputId       = null;
    this.variationInputDiv      = null;
    /*
    *
    */
    this.attributes           = null;
    /*
    *
    */
    this.available_attributes = null;

    if (config) {
      var self = this;
      // init variables
      this.playerDivId            = config.playerDivId;
      this.configuratorDivId      = config.configuratorDivId;
      this.claraSceneId           = config.claraSceneId;
      this.attributes             = config.attributes;
      this.available_attributes   = config.available_attributes;
      this.configuratorInputDivId = config.configuratorInputDivId;
      this.addtocartClassName     = config.addtocartClassName;
      this.variation_idClassName  = config.variation_idClassName;
      this.variationInputId       = config.variationInputId;

      this.variation_idInput = document.getElementsByClassName(this.variation_idClassName)[0];
      this.variationInputDiv = document.getElementById(this.variationInputId);
      this.addtocartButton = document.getElementsByClassName(this.addtocartClassName)[0];

      this.addtocartButton.onclick = (ev) => {
        if (!self.addtocartEnabled) {
          event.preventDefault();
        }
      };

    }
    console.log(this.attributes);
    console.log(this.available_attributes);
    this._initClara();
  }

  _initClara() {
    var self = this;
    // Fetch and initialize the sceneId
    this.api = claraplayer(this.playerDivId);
    this.api.sceneIO
      .fetchAndUse(this.claraSceneId, null, { waitForPublish: true })
      .then(() => {

        // use the first form in the scene
        self._getConfiguratorForm();

        var configuratorEl = document.getElementById(self.configuratorDivId);

        if (self.configuratorForm && configuratorEl) {
          self.api.configuration.initConfigurator({
            id    : self.claraSceneId,
            form  : self.configuratorForm,
            el    : configuratorEl
          });

          self.api.on('configurationChange', (ev) => {
            self._onConfigurationChange();
          });
        }
      });
  }

  _getConfiguratorForm() {
    if (!this.api) {
      return;
    }
    var forms = this.api.configuration.getForms();
    if (forms.length > 0) {
      this.configuratorForm = forms[0].name;
    }
  }

  _onConfigurationChange() {
    var self = this;
    var config = this.api.configuration.getConfiguration();
    console.log(config);

    var configuratorInputDiv = document.getElementById(this.configuratorInputDivId);
    if (!configuratorInputDiv) {
      return;
    }

    // check if config attribute name and value exist for the current product
    var additionalAttrs = [];
    var attrValueTobeSubmited = [];
    if (Object.keys(this.attributes).length < Object.keys(config).length) {
      console.warn("Threekit attribute number is smaller than in WooCommerce, product will not be able to added to cart");
    }
    for (var key in config) {
      // looking for "key" in attributes
      var found = false;
      var legal = true;
      for (var ele in this.attributes) {
        // remove "pa_"
        var trimEle = ele;
        if (trimEle.startsWith('pa_')) {
          trimEle = trimEle.substr(3);
        }
        if (this.ignoreCaseStrcmp(trimEle, key)) {
          found = true;
          if (!this.ignoreCaseIncludes(this.attributes[ele], config[key])) {
            // config attribute value is illegal
            legal = false;
          }
          break;
        }
      }
      if (!legal) {
        this._disableAddtocartButton();
        return;
      }
      if (!found) {
        // additional attributes will be posted to server as text
        additionalAttrs.push(key);
      }
    }

    /*
    *  Check if the product is available
    *  Attributes in WooCommerce can be overlapping,
    *  using the first matching attribute in this.available_attributes
    */
    var foundMatch = false;
    var selectedVaration = {};
    for (var i = 0; i < this.available_attributes.length; i++) {
      var attrs = this.available_attributes[i].attributes;
      if (Object.keys(attrs).length != Object.keys(config).length) {
        continue;
      }
      var match = true;
      selectedVaration = {};
      for (var key in config) {
        var found = false;
        for(var ele in attrs) {
          // remove "attribute_pa_"
          var trimEle = ele;
          if (trimEle.startsWith('attribute_pa_')) {
            trimEle = trimEle.substr(13);
          }
          if (this.ignoreCaseStrcmp(trimEle, key)) {
            if (attrs[ele] === "" || this.ignoreCaseStrcmp(attrs[ele], config[key])) {
              found = true;
              selectedVaration[ele] = config[key].toLowerCase();
              break;
            }
          }
        }
        if (!found) {
          // matching fail
          match = false;
          break;
        }
      }
      if (match) {
        // find a match!
        foundMatch = true;
        console.log(this.available_attributes[i].variation_id);
        this._enableAddtocartButton();
        this.variation_idInput.setAttribute('value', this.available_attributes[i].variation_id);
        for (var key in selectedVaration) {
          var keyInput = document.createElement('input');
          keyInput.setAttribute('name', key);
          keyInput.setAttribute('value', selectedVaration[key]);
          this.variationInputDiv.appendChild(keyInput);
        }
        break;
      }
    }
    if (!foundMatch) {
      this._disableAddtocartButton();
    }

    // calculate price

  }

  _disableAddtocartButton() {
    if (!this.addtocartButton) {
      return;
    }
    this.addtocartEnabled = false;
    this.addtocartButton.classList.add('disabled');
  }

  _enableAddtocartButton() {
    if (!this.addtocartButton) {
      return;
    }
    this.addtocartEnabled = true;
    this.addtocartButton.classList.remove('disabled');
  }

  /*
  * Utility functions
  */
  ignoreCaseStrcmp(str1, str2) {
    return str1.toLowerCase() === str2.toLowerCase();
  }
  ignoreCaseIncludes(arr, ele) {
    var self = this;
    for (var iter in arr) {
      if (self.ignoreCaseStrcmp(arr[iter], ele)) {
        return true;
      }
    }
    return false;
  }
}

(function() {
  var opts = {
    addtocartClassName    : 'single_add_to_cart_button',
    variation_idClassName : 'variation_id',
    variationInputId      : 'threekit-add-to-cart-inputs';
    playerDivId           : 'clara-player',
    configuratorDivId     : 'panel-embed',
    configuratorInputDivId: 'threekit-add-to-cart-inputs',
    claraSceneId          : php_vars.clarauuid,
    available_attributes  : php_vars.available_attributes,
    attributes            : php_vars.attributes
  };
  var cc = new claraConfigurator(opts);

}());
<|endoftext|>