<commit_before>import {cryptoRandomObjectId} from 'apify-shared/utilities';
import log from 'apify-shared/log';
import {checkParamOrThrow} from 'apify-client/build/utils';
import tough from 'tough-cookie';
import EVENTS from './events';
import {STATUS_CODES_BLOCKED} from '../constants';
import {getCookiesFromResponse} from './session_utils';

const {Cookie} = tough;

const PUPPETEER_COOKIE_TYPE = 1;
const TOUGH_COOKIE_TYPE = 2;

/**
 *  Class aggregating data for session.
 *  Sessions are used to store information such as cookies and can be used for generating fingerprints and proxy sessions.
 *  You can imagine each session as a specific user, with its own cookies, IP (via proxy) and potentially a unique browser fingerprint.
 *  Session internal state can be enriched with custom user data for example some authorization tokens and specific headers in general.
 */
export class Session {
    /**
     * Session configuration.
     * @param [options.id] {String} - Id of session used for generating fingerprints. It is used as proxy session name.
     * @param [options.maxAgeSecs=3000] {Number} - Number of seconds after which the session is considered as expired.
     * @param options.userData {Object} - Object where custom user data can be stored. For example custom headers.
     * @param [options.maxErrorScore=3] {number} - Maximum number of marking session as blocked usage.
     * If the `errorScore` reaches the `maxErrorScore` session is marked as block and it is thrown away.
     * It starts at 0. Calling the `markBad` function increases the `errorScore` by 1.
     * Calling the `markGood` will decrease the `errorScore` by `errorScoreDecrement`
     * @param [options.errorScoreDecrement=0.5] {number} - It is used for healing the session.
     * For example: if your session is marked bad two times, but it is successful on the third attempt it's errorScore is decremented by this number.
     * @param options.createdAt {Date} - Date of creation.
     * @param options.expiredAt {Date} - Date of expiration.
     * @param [options.usageCount=0] {Number} - Indicates how many times the session has been used.
     * @param [options.errorCount=0] {Number} - Indicates how many times the session is marked bad.
     * @param [options.maxUsageCount=50] {Number} - Session should be used only a limited amount of times.
     * This number indicates how many times the session is going to be used, before it is thrown away.
     * @param options.sessionPool {EventEmitter} - SessionPool instance. Session will emit the `sessionRetired` event on this instance.
     */
    constructor(options = {}) {
        const {
            id = `session_${cryptoRandomObjectId(10)}`,
            cookies = [], // @TODO: Delete, deprecate or leave it as custom cookie persistance?
            cookieJar = new tough.CookieJar(),
            maxAgeSecs = 3000,
            userData = {},
            maxErrorScore = 3,
            errorScoreDecrement = 0.5,
            createdAt = new Date(),
            usageCount = 0,
            errorScore = 0,
            maxUsageCount = 50,
            sessionPool,
        } = options;

        const {expiresAt = new Date(Date.now() + (maxAgeSecs * 1000))} = options;
        const type = options.type ? parseInt(options.type, 10) : TOUGH_COOKIE_TYPE;

        // Validation
        checkParamOrThrow(id, 'options.id', 'String');
        checkParamOrThrow(maxAgeSecs, 'options.maxAgeSecs', 'Number');
        checkParamOrThrow(userData, 'options.userData', 'Object');
        checkParamOrThrow(maxErrorScore, 'options.maxErrorScore', 'Number');
        checkParamOrThrow(expiresAt, 'options.expiresAt', 'Maybe Date');
        checkParamOrThrow(createdAt, 'options.createdAt', 'Date');
        checkParamOrThrow(usageCount, 'options.usageCount', 'Number');
        checkParamOrThrow(errorScore, 'options.errorScore', 'Number');
        checkParamOrThrow(maxUsageCount, 'options.maxUsageCount', 'Number');
        checkParamOrThrow(sessionPool, 'options.sessionPool', 'Object');
        checkParamOrThrow(type, 'options.type', 'Number');

        // sessionPool must be instance of SessionPool.
        if (sessionPool.constructor.name !== 'SessionPool') {
            throw new Error('Session: sessionPool must be instance of SessionPool');
        }

        // Configurable
        this.id = id;
        this.cookies = cookies;
        this.cookieJar = cookieJar.setCookie ? cookieJar : tough.CookieJar.fromJSON(JSON.stringify(cookieJar));
        this.maxAgeSecs = maxAgeSecs;
        this.userData = userData;
        this.maxErrorScore = maxErrorScore;
        this.errorScoreDecrement = errorScoreDecrement;

        // Internal
        this.expiresAt = expiresAt;
        this.createdAt = createdAt;
        this.usageCount = usageCount; // indicates how many times the session has been used
        this.errorScore = errorScore; // indicates number of markBaded request with the session
        this.maxUsageCount = maxUsageCount;
        this.sessionPool = sessionPool;
        this.type = type;
    }

    /**
     * indicates whether the session is blocked.
     * Session is blocked once it reaches the `maxErrorScore`.
     * @return {boolean}
     */
    isBlocked() {
        return this.errorScore >= this.maxErrorScore;
    }

    /**
     * Indicates whether the session is expired.
     * Session expiration is determined by the `maxAgeSecs`.
     * Once the session is older than `createdAt + maxAgeSecs` the session is considered expired.
     * @return {boolean}
     */
    isExpired() {
        return this.expiresAt <= new Date();
    }

    /**
     * Indicates whether the session is used maximum number of times.
     * Session maximum usage count can be changed by `maxUsageCount` parameter.
     * @return {boolean}
     */
    isMaxUsageCountReached() {
        return this.usageCount >= this.maxUsageCount;
    }

    /**
     * Indicates whether the session can be used for next requests.
     * Session is usable when it is not expired, not blocked and the maximum usage count has not be reached.
     * @return {boolean}
     */
    isUsable() {
        return !this.isBlocked() && !this.isExpired() && !this.isMaxUsageCountReached();
    }

    /**
     * This method should be called after a successful session usage.
     * It increases `usageCount` and potentially lowers the `errorScore` by the `errorScoreDecrement`.
     */
    markGood() {
        this.usageCount += 1;

        if (this.errorScore > 0) {
            this.errorScore -= this.errorScoreDecrement;
        }
    }

    /**
     * Gets session state for persistence in KeyValueStore.
     * @return {Object} represents session internal state.
     */
    getState() {
        return {
            id: this.id,
            cookies: this.cookies,
            cookieJar: this.cookieJar.toJSON(),
            userData: this.userData,
            maxErrorScore: this.maxErrorScore,
            errorScoreDecrement: this.errorScoreDecrement,
            expiresAt: this.expiresAt.toISOString(),
            createdAt: this.createdAt.toISOString(),
            usageCount: this.usageCount,
            errorScore: this.errorScore,
            type: this.type,
        };
    }

    /**
     * Marks session as blocked and emits event on the `SessionPool`
     * This method should be used if the session usage was unsuccessful
     * and you are sure that it is because of the session configuration and not any external matters.
     * For example when server returns 403 status code.
     * If the session does not work due to some external factors as server error such as 5XX you probably want to use `markBad` method.
     */
    retire() {
        // mark it as an invalid by increasing the error score count.
        this.errorScore += this.maxErrorScore;
        this.usageCount += 1;

        // emit event so we can retire browser in puppeteer pool
        this.sessionPool.emit(EVENTS.SESSION_RETIRED, this);
    }

    /**
     * Increases usage and error count.
     * Should be used when the session has been used unsuccessfully. For example because of timeouts.
     */
    markBad() {
        this.errorScore += 1;
        this.usageCount += 1;
    }

    /**
     * Retires session based on status code.
     * @param statusCode {Number} - HTTP status code
     * @return {boolean} whether the session was retired.
     */
    checkStatus(statusCode) {
        const isBlocked = STATUS_CODES_BLOCKED.includes(statusCode);
        if (isBlocked) {
            this.retire();
        }
        return isBlocked;
    }

    /**
     * Sets cookies from response to the cookieJar.
     * Accepts responses with headers accessible by `response.headers` or `response.headers()`.
     * Parses cookies from `set-cookie` header and sets them to `Session.cookieJar`.
     * @param response
     */
    setCookiesFromResponse(response) {
        try {
            const cookies = getCookiesFromResponse(response).filter(c => c);

            this.setCookies(cookies, response.url);
        } catch (e) {
            // if invalid Cookie header is provided just log the exception.
            log.exception(e, 'Session: Could not get cookies from response');
        }
    }

    /**
     * Set cookies to session cookieJar.
     * Cookies array should be [tough-cookie](https://github.com/salesforce/tough-cookie)
     * or [puppeteer](https://pptr.dev/#?product=Puppeteer&version=v2.0.0&show=api-pagecookiesurls) cookie compatible.
     * @param cookies {Array<Cookie>}
     * @param url {String}
     */
    setCookies(cookies, url) {
        const isPuppeteerCookies = !!cookies[0].name;

        for (const cookie of cookies) {
            let cookieToSave;

            if (isPuppeteerCookies) {
                cookieToSave = this._puppeteerCookieToTough(cookie);
                this.type = PUPPETEER_COOKIE_TYPE;
            } else {
                cookieToSave = cookie;
                this.type = TOUGH_COOKIE_TYPE;
            }

            this.cookieJar.setCookieSync(cookieToSave, url, {ignoreError: false});
        }
    }

    /**
     * Get cookies.
     * Gets a array of cookies in the format corresponding to the type property.
     * @param url {String}
     * @return {Array<Object>}
     */
    getCookies(url) {
        const shouldTransformToPuppeteerCookie = this.type === PUPPETEER_COOKIE_TYPE;
        const cookies = this.cookieJar.getCookiesSync(url);

        if (!shouldTransformToPuppeteerCookie) {
            return cookies;
        }

        return cookies.map(this._toughCookieToPuppeteer);
    }

    /**
     * Wrapper around `tough-cookie` Cookie jar `getCookieString` method.
     * @param url
     * @return {String} - represents `Cookie` header.
     */
    getCookieString(url) {
        return this.cookieJar.getCookieStringSync(url, {});
    }

    /**
     *  Transforms puppeteer cookie to tough-cookie.
     * @param puppeteerCookie {Object} - Cookie from puppeteer `page.cookies method.
     * @return {Cookie}
     * @private
     */
    _puppeteerCookieToTough(puppeteerCookie) {
        return new Cookie({
            key: puppeteerCookie.name,
            value: puppeteerCookie.value,
            expires: new Date(puppeteerCookie.expires),
            domain: puppeteerCookie.domain,
            path: puppeteerCookie.path,
            secure: puppeteerCookie.secure,
            httpOnly: puppeteerCookie.httpOnly,
        });
    }

    /**
     *  Transforms tough-cookie cookie to puppeteer Cookie .
     * @param toughCookie - Cookie from CookieJar.
     * @return {Object} - puppeteer cookie
     * @private
     */
    _toughCookieToPuppeteer(toughCookie) {
        return {
            name: toughCookie.key,
            value: toughCookie.value,
            expires: new Date(toughCookie.expires).getTime(),
            domain: toughCookie.domain,
            path: toughCookie.path,
            secure: toughCookie.secure,
            httpOnly: toughCookie.httpOnly,
        };
    }
}
<commit_msg>fix the merge formating.<commit_after>import { cryptoRandomObjectId } from 'apify-shared/utilities';
import log from 'apify-shared/log';
import { checkParamOrThrow } from 'apify-client/build/utils';
import tough from 'tough-cookie';
import EVENTS from './events';
import { STATUS_CODES_BLOCKED } from '../constants';
import { getCookiesFromResponse } from './session_utils';

const { Cookie } = tough;

const PUPPETEER_COOKIE_TYPE = 1;
const TOUGH_COOKIE_TYPE = 2;

/**
 *  Class aggregating data for session.
 *  Sessions are used to store information such as cookies and can be used for generating fingerprints and proxy sessions.
 *  You can imagine each session as a specific user, with its own cookies, IP (via proxy) and potentially a unique browser fingerprint.
 *  Session internal state can be enriched with custom user data for example some authorization tokens and specific headers in general.
 */
export class Session {
    /**
     * Session configuration.
     * @param [options.id] {String} - Id of session used for generating fingerprints. It is used as proxy session name.
     * @param [options.maxAgeSecs=3000] {Number} - Number of seconds after which the session is considered as expired.
     * @param options.userData {Object} - Object where custom user data can be stored. For example custom headers.
     * @param [options.maxErrorScore=3] {number} - Maximum number of marking session as blocked usage.
     * If the `errorScore` reaches the `maxErrorScore` session is marked as block and it is thrown away.
     * It starts at 0. Calling the `markBad` function increases the `errorScore` by 1.
     * Calling the `markGood` will decrease the `errorScore` by `errorScoreDecrement`
     * @param [options.errorScoreDecrement=0.5] {number} - It is used for healing the session.
     * For example: if your session is marked bad two times, but it is successful on the third attempt it's errorScore is decremented by this number.
     * @param options.createdAt {Date} - Date of creation.
     * @param options.expiredAt {Date} - Date of expiration.
     * @param [options.usageCount=0] {Number} - Indicates how many times the session has been used.
     * @param [options.errorCount=0] {Number} - Indicates how many times the session is marked bad.
     * @param [options.maxUsageCount=50] {Number} - Session should be used only a limited amount of times.
     * This number indicates how many times the session is going to be used, before it is thrown away.
     * @param options.sessionPool {EventEmitter} - SessionPool instance. Session will emit the `sessionRetired` event on this instance.
     */
    constructor(options = {}) {
        const {
            id = `session_${cryptoRandomObjectId(10)}`,
            cookies = [], // @TODO: Delete, deprecate or leave it as custom cookie persistance?
            cookieJar = new tough.CookieJar(),
            maxAgeSecs = 3000,
            userData = {},
            maxErrorScore = 3,
            errorScoreDecrement = 0.5,
            createdAt = new Date(),
            usageCount = 0,
            errorScore = 0,
            maxUsageCount = 50,
            sessionPool,
        } = options;

        const { expiresAt = new Date(Date.now() + (maxAgeSecs * 1000)) } = options;
        const type = options.type ? parseInt(options.type, 10) : TOUGH_COOKIE_TYPE;

        // Validation
        checkParamOrThrow(id, 'options.id', 'String');
        checkParamOrThrow(maxAgeSecs, 'options.maxAgeSecs', 'Number');
        checkParamOrThrow(userData, 'options.userData', 'Object');
        checkParamOrThrow(maxErrorScore, 'options.maxErrorScore', 'Number');
        checkParamOrThrow(expiresAt, 'options.expiresAt', 'Maybe Date');
        checkParamOrThrow(createdAt, 'options.createdAt', 'Date');
        checkParamOrThrow(usageCount, 'options.usageCount', 'Number');
        checkParamOrThrow(errorScore, 'options.errorScore', 'Number');
        checkParamOrThrow(maxUsageCount, 'options.maxUsageCount', 'Number');
        checkParamOrThrow(sessionPool, 'options.sessionPool', 'Object');
        checkParamOrThrow(type, 'options.type', 'Number');

        // sessionPool must be instance of SessionPool.
        if (sessionPool.constructor.name !== 'SessionPool') {
            throw new Error('Session: sessionPool must be instance of SessionPool');
        }

        // Configurable
        this.id = id;
        this.cookies = cookies;
        this.cookieJar = cookieJar.setCookie ? cookieJar : tough.CookieJar.fromJSON(JSON.stringify(cookieJar));
        this.maxAgeSecs = maxAgeSecs;
        this.userData = userData;
        this.maxErrorScore = maxErrorScore;
        this.errorScoreDecrement = errorScoreDecrement;

        // Internal
        this.expiresAt = expiresAt;
        this.createdAt = createdAt;
        this.usageCount = usageCount; // indicates how many times the session has been used
        this.errorScore = errorScore; // indicates number of markBaded request with the session
        this.maxUsageCount = maxUsageCount;
        this.sessionPool = sessionPool;
        this.type = type;
    }

    /**
     * indicates whether the session is blocked.
     * Session is blocked once it reaches the `maxErrorScore`.
     * @return {boolean}
     */
    isBlocked() {
        return this.errorScore >= this.maxErrorScore;
    }

    /**
     * Indicates whether the session is expired.
     * Session expiration is determined by the `maxAgeSecs`.
     * Once the session is older than `createdAt + maxAgeSecs` the session is considered expired.
     * @return {boolean}
     */
    isExpired() {
        return this.expiresAt <= new Date();
    }

    /**
     * Indicates whether the session is used maximum number of times.
     * Session maximum usage count can be changed by `maxUsageCount` parameter.
     * @return {boolean}
     */
    isMaxUsageCountReached() {
        return this.usageCount >= this.maxUsageCount;
    }

    /**
     * Indicates whether the session can be used for next requests.
     * Session is usable when it is not expired, not blocked and the maximum usage count has not be reached.
     * @return {boolean}
     */
    isUsable() {
        return !this.isBlocked() && !this.isExpired() && !this.isMaxUsageCountReached();
    }

    /**
     * This method should be called after a successful session usage.
     * It increases `usageCount` and potentially lowers the `errorScore` by the `errorScoreDecrement`.
     */
    markGood() {
        this.usageCount += 1;

        if (this.errorScore > 0) {
            this.errorScore -= this.errorScoreDecrement;
        }
    }

    /**
     * Gets session state for persistence in KeyValueStore.
     * @return {Object} represents session internal state.
     */
    getState() {
        return {
            id: this.id,
            cookies: this.cookies,
            cookieJar: this.cookieJar.toJSON(),
            userData: this.userData,
            maxErrorScore: this.maxErrorScore,
            errorScoreDecrement: this.errorScoreDecrement,
            expiresAt: this.expiresAt.toISOString(),
            createdAt: this.createdAt.toISOString(),
            usageCount: this.usageCount,
            errorScore: this.errorScore,
            type: this.type,
        };
    }

    /**
     * Marks session as blocked and emits event on the `SessionPool`
     * This method should be used if the session usage was unsuccessful
     * and you are sure that it is because of the session configuration and not any external matters.
     * For example when server returns 403 status code.
     * If the session does not work due to some external factors as server error such as 5XX you probably want to use `markBad` method.
     */
    retire() {
        // mark it as an invalid by increasing the error score count.
        this.errorScore += this.maxErrorScore;
        this.usageCount += 1;

        // emit event so we can retire browser in puppeteer pool
        this.sessionPool.emit(EVENTS.SESSION_RETIRED, this);
    }

    /**
     * Increases usage and error count.
     * Should be used when the session has been used unsuccessfully. For example because of timeouts.
     */
    markBad() {
        this.errorScore += 1;
        this.usageCount += 1;
    }

    /**
     * Retires session based on status code.
     * @param statusCode {Number} - HTTP status code
     * @return {boolean} whether the session was retired.
     */
    checkStatus(statusCode) {
        const isBlocked = STATUS_CODES_BLOCKED.includes(statusCode);
        if (isBlocked) {
            this.retire();
        }
        return isBlocked;
    }

    /**
     * Sets cookies from response to the cookieJar.
     * Accepts responses with headers accessible by `response.headers` or `response.headers()`.
     * Parses cookies from `set-cookie` header and sets them to `Session.cookieJar`.
     * @param response
     */
    setCookiesFromResponse(response) {
        try {
            const cookies = getCookiesFromResponse(response).filter(c => c);

            this.setCookies(cookies, response.url);
        } catch (e) {
            // if invalid Cookie header is provided just log the exception.
            log.exception(e, 'Session: Could not get cookies from response');
        }
    }

    /**
     * Set cookies to session cookieJar.
     * Cookies array should be [tough-cookie](https://github.com/salesforce/tough-cookie)
     * or [puppeteer](https://pptr.dev/#?product=Puppeteer&version=v2.0.0&show=api-pagecookiesurls) cookie compatible.
     * @param cookies {Array<Cookie>}
     * @param url {String}
     */
    setCookies(cookies, url) {
        const isPuppeteerCookies = !!cookies[0].name;

        for (const cookie of cookies) {
            let cookieToSave;

            if (isPuppeteerCookies) {
                cookieToSave = this._puppeteerCookieToTough(cookie);
                this.type = PUPPETEER_COOKIE_TYPE;
            } else {
                cookieToSave = cookie;
                this.type = TOUGH_COOKIE_TYPE;
            }

            this.cookieJar.setCookieSync(cookieToSave, url, { ignoreError: false });
        }
    }

    /**
     * Get cookies.
     * Gets a array of cookies in the format corresponding to the type property.
     * @param url {String}
     * @return {Array<Object>}
     */
    getCookies(url) {
        const shouldTransformToPuppeteerCookie = this.type === PUPPETEER_COOKIE_TYPE;
        const cookies = this.cookieJar.getCookiesSync(url);

        if (!shouldTransformToPuppeteerCookie) {
            return cookies;
        }

        return cookies.map(this._toughCookieToPuppeteer);
    }

    /**
     * Wrapper around `tough-cookie` Cookie jar `getCookieString` method.
     * @param url
     * @return {String} - represents `Cookie` header.
     */
    getCookieString(url) {
        return this.cookieJar.getCookieStringSync(url, {});
    }

    /**
     *  Transforms puppeteer cookie to tough-cookie.
     * @param puppeteerCookie {Object} - Cookie from puppeteer `page.cookies method.
     * @return {Cookie}
     * @private
     */
    _puppeteerCookieToTough(puppeteerCookie) {
        return new Cookie({
            key: puppeteerCookie.name,
            value: puppeteerCookie.value,
            expires: new Date(puppeteerCookie.expires),
            domain: puppeteerCookie.domain,
            path: puppeteerCookie.path,
            secure: puppeteerCookie.secure,
            httpOnly: puppeteerCookie.httpOnly,
        });
    }

    /**
     *  Transforms tough-cookie cookie to puppeteer Cookie .
     * @param toughCookie - Cookie from CookieJar.
     * @return {Object} - puppeteer cookie
     * @private
     */
    _toughCookieToPuppeteer(toughCookie) {
        return {
            name: toughCookie.key,
            value: toughCookie.value,
            expires: new Date(toughCookie.expires).getTime(),
            domain: toughCookie.domain,
            path: toughCookie.path,
            secure: toughCookie.secure,
            httpOnly: toughCookie.httpOnly,
        };
    }
}
<|endoftext|>


<commit_before>// controller used by datatype editor

angular
    .module("umbraco")
    .controller("nuComponents.DataTypes.Shared.ListPicker.ListPickerEditorController",
        ['$scope', 'nuComponents.DataTypes.Shared.ListPicker.ListPickerResource',
        function ($scope, listPickerResource) {

            /*
                expects to find:

                    $scope.model.config.listPicker = {
                                "cssFile":null,
                                "scriptFile":null,
                                "listHeight":null,
                                "minItems":"0",
                                "maxItems":"0",
                                "allowDuplicates":"false",
                                "hideUsed":"false",
                                "enableFiltering":"true"}
                            }

                    $scope.model.condig.listPickerApiController = "XPathTemplatableListApi"
            */

            // array of option objects, for the selectable list
            $scope.selectableOptions = []; // [{"key":"","markup":""}...]

            // array of option objects, for the selected list
            $scope.selectedOptions = []; // [{"key":"","markup":""}...]

            // http://api.jqueryui.com/sortable/
            $scope.sortableConfiguration = { axis: 'y' };

            // returns true if option hans't yet been picked, or duplicates are allowed
            $scope.isSelectable = function (option) {
                return ($scope.model.config.listPicker.allowDuplicates || !$scope.isUsed(option));
            };

            // returns true is this option has been picked
            $scope.isUsed = function (option) {
                return $scope.selectedOptions.indexOf(option) >= 0;
            };

            // return true is option can be picked
            $scope.isValidSelection = function (option) {
                return $scope.isSelectable(option) && ($scope.selectedOptions.length < $scope.model.config.listPicker.maxItems || $scope.model.config.listPicker.maxItems <= 0);
            };

            // picking an item from 'selectable' for 'selected'
            $scope.selectOption = function (option) {

                // if item can be selected and not exceeding the max
                if ($scope.isValidSelection(option)) {
                    $scope.selectedOptions.push(option);
                }
            };

            // count for dashed placeholders
            $scope.getRequiredPlaceholderCount = function () {
                var count = $scope.model.config.listPicker.minItems - $scope.selectedOptions.length;
                if (count > 0) { return new Array(count); }
                return null;
            }

            $scope.showSelectPlaceholder = function () {

                return ($scope.selectedOptions.length >= $scope.model.config.listPicker.minItems)
                        && (($scope.selectedOptions.length < $scope.model.config.listPicker.maxItems) || $scope.model.config.listPicker.maxItems == 0);
                // TODO: and selectable options, has valid options
            }

            // return true if there is more than 1 item in the selected list
            $scope.isSortable = function () {
                return $scope.selectedOptions.length > 1; // TODO: check selectedOptions are not all the same
            };

            // remove option from 'selected'
            $scope.deselectOption = function ($index) {
                $scope.selectedOptions.splice($index, 1);
            };

            // call api, supplying all configuration details, and expect a collection of options (key / markup) to be populated
            //pickerResource.getEditorOptions($scope.model.config.configuration).then(function (response) {
            listPickerResource.getEditorOptions($scope.model.config).then(function (response) {

                var editorOptions = response.data; // [{"key":"","markup":""},{"key":"","markup":""}...]

                // build selected options (from saved csv)
                var savedKeys = $scope.model.value.split(',');
                for (var i = 0; i < savedKeys.length; i++) { // loop though each saved key
                    for (var j = 0; j < editorOptions.length; j++) { // loop though each editor option
                        if (savedKeys[i] == editorOptions[j].key) {
                            $scope.selectedOptions.push(editorOptions[j]);
                            break; // break out of the editor option loop
                        }
                    }
                }

                // setup watch on selected options
                $scope.$watchCollection('selectedOptions', function () {

                    //recreate the csv in model.value for Umbraco - TODO: json, xml, or csv
                    $scope.model.value = $scope.selectedOptions.map(function (option) { return option.key; }).join();

                    // TODO: how to return error to Umbraco ?
                    //var isValid = ($scope.selectableOptions.length >= $scope.model.config.minItems
                    //               && ($scope.selectableOptions.length <= $scope.model.config.maxItems || $scope.model.config.maxItems < 1));

                    // toggle sorting ui
                    $scope.sortableConfiguration.disabled = !$scope.isSortable();
                });

                // build selectable options
                $scope.selectableOptions = editorOptions;

                // setup filtering
                if ($scope.model.config.listPicker.enableFiltering) {
                    $scope.allSelectableOptions = $scope.selectableOptions;

                    $scope.$watch('model.filterQuery', function (newValue, oldValue) {
                        if (newValue == null || newValue.length == 0)
                            return $scope.selectableOptions = $scope.allSelectableOptions;

                        $scope.selectableOptions = $scope.allSelectableOptions.filter(function (item) {

                            //TODO: prevent typing that would yeild no matches

                            return item.markup.toLowerCase().indexOf(newValue) != -1;
                        });
                    });
                }
            });

        }]);<commit_msg>List Picker - prevent sorting unless there at least two different items selected<commit_after>// controller used by datatype editor

angular
    .module("umbraco")
    .controller("nuComponents.DataTypes.Shared.ListPicker.ListPickerEditorController",
        ['$scope', 'nuComponents.DataTypes.Shared.ListPicker.ListPickerResource',
        function ($scope, listPickerResource) {

            /*
                expects to find:

                    $scope.model.config.listPicker = {
                                "cssFile":null,
                                "scriptFile":null,
                                "listHeight":null,
                                "minItems":"0",
                                "maxItems":"0",
                                "allowDuplicates":"false",
                                "hideUsed":"false",
                                "enableFiltering":"true"}
                            }

                    $scope.model.condig.listPickerApiController = "XPathTemplatableListApi"
            */

            // array of option objects, for the selectable list
            $scope.selectableOptions = []; // [{"key":"","markup":""}...]

            // array of option objects, for the selected list
            $scope.selectedOptions = []; // [{"key":"","markup":""}...]

            // http://api.jqueryui.com/sortable/
            $scope.sortableConfiguration = { axis: 'y' };

            // returns true if option hans't yet been picked, or duplicates are allowed
            $scope.isSelectable = function (option) {
                return ($scope.model.config.listPicker.allowDuplicates || !$scope.isUsed(option));
            };

            // returns true is this option has been picked
            $scope.isUsed = function (option) {
                return $scope.selectedOptions.indexOf(option) >= 0;
            };

            // return true is option can be picked
            $scope.isValidSelection = function (option) {
                return $scope.isSelectable(option) && ($scope.selectedOptions.length < $scope.model.config.listPicker.maxItems || $scope.model.config.listPicker.maxItems <= 0);
            };

            // picking an item from 'selectable' for 'selected'
            $scope.selectOption = function (option) {

                // if item can be selected and not exceeding the max
                if ($scope.isValidSelection(option)) {
                    $scope.selectedOptions.push(option);
                }
            };

            // count for dashed placeholders
            $scope.getRequiredPlaceholderCount = function () {
                var count = $scope.model.config.listPicker.minItems - $scope.selectedOptions.length;
                if (count > 0) { return new Array(count); }
                return null;
            }

            $scope.showSelectPlaceholder = function () {

                return ($scope.selectedOptions.length >= $scope.model.config.listPicker.minItems)
                        && (($scope.selectedOptions.length < $scope.model.config.listPicker.maxItems) || $scope.model.config.listPicker.maxItems == 0);
                // TODO: and selectable options, has valid options
            }

            // return true if there is are at least two different items selected
            $scope.isSortable = function () {

                if (!$scope.model.config.allowDuplicates && $scope.selectedOptions > 1) {
                    return true;
                }

                // else check collection contains at least two different items
                var keys = $scope.selectedOptions.map(function (option) { return option.key; }); // key all selected keys

                return keys.filter(function (value, index) { return keys.indexOf(value) == index; }).length >= 2;
            };

            // remove option from 'selected'
            $scope.deselectOption = function ($index) {
                $scope.selectedOptions.splice($index, 1);
            };

            // call api, supplying all configuration details, and expect a collection of options (key / markup) to be populated
            //pickerResource.getEditorOptions($scope.model.config.configuration).then(function (response) {
            listPickerResource.getEditorOptions($scope.model.config).then(function (response) {

                var editorOptions = response.data; // [{"key":"","markup":""},{"key":"","markup":""}...]

                // build selected options (from saved csv)
                var savedKeys = $scope.model.value.split(',');
                for (var i = 0; i < savedKeys.length; i++) { // loop though each saved key
                    for (var j = 0; j < editorOptions.length; j++) { // loop though each editor option
                        if (savedKeys[i] == editorOptions[j].key) {
                            $scope.selectedOptions.push(editorOptions[j]);
                            break; // break out of the editor option loop
                        }
                    }
                }

                // setup watch on selected options
                $scope.$watchCollection('selectedOptions', function () {

                    //recreate the csv in model.value for Umbraco - TODO: json, xml, or csv
                    $scope.model.value = $scope.selectedOptions.map(function (option) { return option.key; }).join();

                    // TODO: how to return error to Umbraco ?
                    //var isValid = ($scope.selectableOptions.length >= $scope.model.config.minItems
                    //               && ($scope.selectableOptions.length <= $scope.model.config.maxItems || $scope.model.config.maxItems < 1));

                    // toggle sorting ui
                    $scope.sortableConfiguration.disabled = !$scope.isSortable();
                });

                // build selectable options
                $scope.selectableOptions = editorOptions;

                // setup filtering
                if ($scope.model.config.listPicker.enableFiltering) {
                    $scope.allSelectableOptions = $scope.selectableOptions;

                    $scope.$watch('model.filterQuery', function (newValue, oldValue) {
                        if (newValue == null || newValue.length == 0)
                            return $scope.selectableOptions = $scope.allSelectableOptions;

                        $scope.selectableOptions = $scope.allSelectableOptions.filter(function (item) {

                            //TODO: prevent typing that would yeild no matches

                            return item.markup.toLowerCase().indexOf(newValue) != -1;
                        });
                    });
                }
            });

        }]);<|endoftext|>


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

angular.module('NourritureControllers')
	.controller('CreateIngredientController', CreateIngredientController);

CreateIngredientController.$inject = ["$scope", "IngredientService", 'TagsService', 'toastr',"$log"];

function CreateIngredientController($scope, IngredientService, TagsService, toastr, $log)
{
	var vm = this;

	$log.log("innit");

	//Vars for Chips
	vm.tags_ingredient = [];
	vm.selectedItemChip = null;
	vm.searchTextChip = null;
	vm.itemsAutocomplete = [];

	vm.submit = function() {
		$log.log("innit");
		IngredientService
			.ingredients
			.save({"name" : $scope.name, "description" : $scope.description, "calories":$scope.calories, "fat" : $scope.fat, "carbohydrates" : $scope.carbohydrates, "proteins" : $scope.proteins})
			.$promise
			.then(vm.IngredientCreateSuccess, vm.IngredientCreateFailure);
	};

	vm.IngredientCreateSuccess = function (data) {
		$log.log(data.message);
		toastr.success('You can now access your new ingredient.', 'Ingredient Created!');
	};

	vm.IngredientCreateFailure = function (data) {
		$log.log(data.data);
		var errorMsg = "This is odd...";
		if (data.data.errmsg.indexOf("name") > -1)
			errorMsg = "Seems like the ingredient already exists";
		toastr.error(errorMsg, 'Woops...');
	};

	vm.TagsGetFailure = function (data) {
		$log.log(data.data);
		var errorMsg = "This is odd...";
		if (data.data.errmsg.indexOf("name") > -1)
			errorMsg = "Seems like the ingredient already exists";
		toastr.error(errorMsg, 'Woops...');
	};

	vm.TagsGetSuccess = function (data) {
		$log.log(data);
		vm.tags_ingredient = data;
	};

	vm.TagsGetNameFailure = function (data) {
		$log.log(data.data);
		var errorMsg = "Tag not found...";
		toastr.error(errorMsg, 'Woops...');
		vm.itemsAutocomplete = [];
		return (vm.itemsAutocomplete);
	};

	vm.TagsGetNameSuccess = function (data) {
		$log.log(data);
		vm.itemsAutocomplete = data;
		return (vm.itemsAutocomplete);
	};

	//Chips functions
	vm.transformChip = function(chip) {
		if (angular.isObject(chip))
			return chip;
		if (chip._id === undefined)
			return {
				name : chip
			}
		else
		{
			$log.log(vm.tags_ingredient);
			for(var i=0; i < vm.tags_ingredient.length; i++)
				if (vm.tags_ingredient[i].name == chip.name)
					return ;
			return {
				name : chip.name
			}
		}
	}

	vm.selectedItemChangeChip = function () {

	}

	vm.getNameTags = function(name) {
		$log.log("innit");
		return (TagsService
			.tags_name
			.query({name : name})
			.$promise
			.then(vm.TagsGetNameSuccess, vm.TagsGetNameFailure));
	}

}

})();<commit_msg>added tags commit to backend<commit_after>(function() {
'use strict';

angular.module('NourritureControllers')
	.controller('CreateIngredientController', CreateIngredientController);

CreateIngredientController.$inject = ["$scope", "IngredientService", 'TagsService', 'toastr',"$log"];

function CreateIngredientController($scope, IngredientService, TagsService, toastr, $log)
{
	var vm = this;

	$log.log("innit");

	//Vars for Chips
	vm.tags_ingredient = [];
	vm.selectedItemChip = null;
	vm.searchTextChip = null;
	vm.itemsAutocomplete = [];

	vm.submit = function() {
		$log.log("innit");
		IngredientService
			.ingredients
			.save({"name" : $scope.name, "description" : $scope.description, "calories":$scope.calories, "fat" : $scope.fat, "carbohydrates" : $scope.carbohydrates, "proteins" : $scope.proteins, "tags" : vm.tags_ingredient})
			.$promise
			.then(vm.IngredientCreateSuccess, vm.IngredientCreateFailure);
	};

	vm.IngredientCreateSuccess = function (data) {
		$log.log(data.message);
		toastr.success('You can now access your new ingredient.', 'Ingredient Created!');
	};

	vm.IngredientCreateFailure = function (data) {
		$log.log(data.data);
		var errorMsg = "This is odd...";
		if (data.data.errmsg.indexOf("name") > -1)
			errorMsg = "Seems like the ingredient already exists";
		toastr.error(errorMsg, 'Woops...');
	};

	vm.TagsGetFailure = function (data) {
		$log.log(data.data);
		var errorMsg = "This is odd...";
		if (data.data.errmsg.indexOf("name") > -1)
			errorMsg = "Seems like the ingredient already exists";
		toastr.error(errorMsg, 'Woops...');
	};

	vm.TagsGetSuccess = function (data) {
		$log.log(data);
		vm.tags_ingredient = data;
	};

	vm.TagsGetNameFailure = function (data) {
		$log.log(data.data);
		var errorMsg = "Tag not found...";
		toastr.error(errorMsg, 'Woops...');
		vm.itemsAutocomplete = [];
		return (vm.itemsAutocomplete);
	};

	vm.TagsGetNameSuccess = function (data) {
		$log.log(data);
		vm.itemsAutocomplete = data;
		return (vm.itemsAutocomplete);
	};

	//Chips functions
	vm.transformChip = function(chip) {
		if (angular.isObject(chip))
			return chip;
		if (chip._id === undefined)
			return {
				name : chip
			}
		else
			return {
				name : chip.name
		}
	}

	vm.getNameTags = function(name) {
		$log.log("innit");
		return (TagsService
			.tags_name
			.query({name : name})
			.$promise
			.then(vm.TagsGetNameSuccess, vm.TagsGetNameFailure));
	}

}

})();<|endoftext|>


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

  var hostRules = [
    /^(([\w]{8}|www)\.)?(allanalpass|cash4files|drstickyfingers|fapoff|freegaysitepass|(gone|tube)viral|(pic|tna)bucks|whackyvidz)\.com$/,
    /^(([\w]{8}|www)\.)?(a[mn]y|deb|dyo|sexpalace)\.gs$/,
    /^(([\w]{8}|www)\.)?(filesonthe|poontown|seriousdeals|ultrafiles|urlbeat)\.net$/,
    /^(([\w]{8}|www)\.)?freean\.us$/,
    /^(([\w]{8}|www)\.)?galleries\.bz$/,
    /^(([\w]{8}|www)\.)?hornywood\.tv$/,
    /^(([\w]{8}|www)\.)?link(babes|bucks)\.com$/,
    /^(([\w]{8}|www)\.)?(megaline|miniurls|qqc|rqq|tinylinks|yyv|zff)\.co$/,
    /^(([\w]{8}|www)\.)?(these(blog|forum)s)\.com$/,
    /^(([\w]{8}|www)\.)?youfap\.me$/,
    /^warning-this-linkcode-will-cease-working-soon\.www\.linkbucksdns\.com$/,
  ];

  $.register({
    rule: {
      host: hostRules,
      path: /^\/\w+\/url\/(.*)$/,
    },
    ready: function(m) {
      $.removeAllTimer();
      $.resetCookies();
      $.removeNodes('iframe');

      if (m.path[1] !== null) {
        $.openLink(m.path[1]);
      }
    }
  });

  $.register({
    rule: {
      host: hostRules,
    },
    ready: function () {
      $.removeAllTimer();
      $.resetCookies();
      $.removeNodes('iframe');

      if (window.location.pathname.indexOf('verify') >= 0) {
        // NOTE dirty fix
        $.openLink('../');
        return;
      }

      var script = $.$$('script').find(function (n) {
        if (n.innerHTML.indexOf('window[\'init\' + \'Lb\' + \'js\' + \'\']') < 0) {
          return _.nop;
        }
        return n.innerHTML;
      });
      if (!script) {
        _.warn('pattern changed');
        return;
      }
      script = script.payload;

      var m = script.match(/AdPopUrl\s*:\s*'.+\?ref=([\w\d]+)'/);
      var token = m[1];
      m = script.match(/=\s*(\d+);/);
      var ak = parseInt(m[1], 10);
      var re = /\+\s*(\d+);/g;
      var tmp = null;
      // get second (i.e. the real) salt
      while((m = re.exec(script)) !== null) {
        tmp = m[1];
      }
      ak += parseInt(tmp, 10);

      _.info({
        t: token,
        aK: ak,
      });

      var i = setInterval(function () {
        $.get('/intermission/loadTargetUrl', {
          t: token,
          aK: ak,
        }, function (text) {
          var data = JSON.parse(text);
          _.info(data);
          if (!data.Success && data.Errors[0] === 'Invalid token') {
            // somehow this token is invalid, reload to get new one
            window.location.reload();
            return;
          }
          if (data.Success && !data.AdBlockSpotted && data.Url) {
            clearInterval(i);
            $.openLink(data.Url);
          }
        });
      }, 1000);
    },
  });
})();

// ex: ts=2 sts=2 sw=2 et
// sublime: tab_size 2; translate_tabs_to_spaces true; detect_indentation false; use_tab_stops true;
// kate: space-indent on; indent-width 2;
<commit_msg>linkbucks.com<commit_after>(function() {
  'use strict';

  var hostRules = [
    /^(([\w]{8}|www)\.)?(allanalpass|cash4files|drstickyfingers|fapoff|freegaysitepass|(gone|tube)viral|(pic|tna)bucks|whackyvidz)\.com$/,
    /^(([\w]{8}|www)\.)?(a[mn]y|deb|dyo|sexpalace)\.gs$/,
    /^(([\w]{8}|www)\.)?(filesonthe|poontown|seriousdeals|ultrafiles|urlbeat)\.net$/,
    /^(([\w]{8}|www)\.)?freean\.us$/,
    /^(([\w]{8}|www)\.)?galleries\.bz$/,
    /^(([\w]{8}|www)\.)?hornywood\.tv$/,
    /^(([\w]{8}|www)\.)?link(babes|bucks)\.com$/,
    /^(([\w]{8}|www)\.)?(megaline|miniurls|qqc|rqq|tinylinks|yyv|zff)\.co$/,
    /^(([\w]{8}|www)\.)?(these(blog|forum)s)\.com$/,
    /^(([\w]{8}|www)\.)?youfap\.me$/,
    /^warning-this-linkcode-will-cease-working-soon\.www\.linkbucksdns\.com$/,
  ];

  function findToken (context) {
    var script = $.$$('script', context).find(function (n) {
      if (n.innerHTML.indexOf('window[\'init\' + \'Lb\' + \'js\' + \'\']') < 0) {
        return _.nop;
      }
      return n.innerHTML;
    });
    if (!script) {
      _.warn('pattern changed');
      return null;
    }
    script = script.payload;

    var m = script.match(/AdPopUrl\s*:\s*'.+\?ref=([\w\d]+)'/);
    var token = m[1];
    m = script.match(/=\s*(\d+);/);
    var ak = parseInt(m[1], 10);
    var re = /\+\s*(\d+);/g;
    var tmp = null;
    // get second (i.e. the real) salt
    while((m = re.exec(script)) !== null) {
      tmp = m[1];
    }
    ak += parseInt(tmp, 10);

    return {
      t: token,
      aK: ak,
    };
  }

  function sendRequest (token) {
    _.info('sending token: %o', token);

    var i = setInterval(function () {
      $.get('/intermission/loadTargetUrl', token, function (text) {
        var data = JSON.parse(text);

        _.info('response: %o', data);

        if (!data.Success && data.Errors[0] === 'Invalid token') {
          // somehow this token is invalid, reload to get new one
          // window.location.reload();
          _.info('got invalid token');
          clearInterval(i);
          $.get(window.location.toString(), {}, function (text) {
            var d = $.toDOM(text);
            var t = findToken(d);
            sendRequest(t);
          });
          return;
        }
        if (data.Success && !data.AdBlockSpotted && data.Url) {
          clearInterval(i);
          $.openLink(data.Url);
          return;
        }
      });
    }, 1000);
  }

  $.register({
    rule: {
      host: hostRules,
      path: /^\/\w+\/url\/(.*)$/,
    },
    ready: function(m) {
      $.removeAllTimer();
      $.resetCookies();
      $.removeNodes('iframe');

      if (m.path[1] !== null) {
        $.openLink(m.path[1]);
      }
    }
  });

  $.register({
    rule: {
      host: hostRules,
    },
    ready: function () {
      $.removeAllTimer();
      $.resetCookies();
      $.removeNodes('iframe');

      if (window.location.pathname.indexOf('verify') >= 0) {
        // NOTE dirty fix
        $.openLink('../');
        return;
      }

      var token = findToken(document);
      sendRequest(token);
    },
  });
})();

// ex: ts=2 sts=2 sw=2 et
// sublime: tab_size 2; translate_tabs_to_spaces true; detect_indentation false; use_tab_stops true;
// kate: space-indent on; indent-width 2;
<|endoftext|>


<commit_before>/*!
 * ${copyright}
 */

sap.ui.define([
	"sap/ui/fl/descriptorRelated/api/DescriptorInlineChangeFactory",
	"sap/ui/fl/Utils",
	"sap/ui/fl/LrepConnector",
	"sap/ui/fl/descriptorRelated/internal/Utils",
	"sap/ui/fl/descriptorRelated/api/Settings"
], function(DescriptorInlineChangeFactory, LrepUtils, LrepConnector, Utils, Settings) {
	"use strict";

	/**
	 * Descriptor Variant
	 *
	 * @param {object} mParameters parameters
	 * @param {string} mParameters.id the id of the descriptor variant id to be provided for a new descriptor variant and for deleting a descriptor variant
	 * @param {string} mParameters.reference the referenced descriptor or descriptor variant id to be provided when creating a new descriptor variant
	 * @param {boolean} [mParameters.isAppVariantRoot=true]
	 * @param {object} mFileContent file content of the existing descriptor variant to be provided if descriptor variant shall be created from an existing
	 * @param {boolean} [bDeletion=false] deletion indicator to be provided if descriptor variant shall be deleted
	 * @param {sap.ui.fl.descriptorRelated.api.Settings} oSettings settings
	 *
	 * @constructor
	 * @alias sap.ui.fl.descriptorRelated.api.DescriptorVariant
	 * @author SAP SE
	 * @version ${version}
	 * @private
	 * @sap-restricted
	 */

	//Descriptor Variant
	var DescriptorVariant = function(mParameters, mFileContent, bDeletion, oSettings) {
		if (mParameters && bDeletion) {
			this._id = mParameters.id;
			this._mode = 'DELETION';
			this._mMap = mFileContent;

		} else if (mParameters) {
			this._id = mParameters.id;
			this._reference = mParameters.reference;
			this._layer = mParameters.layer;
			if ( typeof mParameters.isAppVariantRoot != undefined){
				this._isAppVariantRoot = mParameters.isAppVariantRoot;
			}
			this._mode = 'NEW';

		} else if (mFileContent) {
			this._mMap = mFileContent;
			this._mode = 'FROM_EXISTING';

		}

		this._oSettings = oSettings;
		this._sTransportRequest = null;
		this._content = [];
	};

	/**
	 * Adds a descriptor inline change to the descriptor variant
	 *
	 * @param {sap.ui.fl.descriptorRelated.api.DescriptorInlineChange} oDescriptorInlineChange the inline change
	 *
	 * @return {Promise} resolving when adding the descriptor inline change was successful (without backend access)
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariant.prototype.addDescriptorInlineChange = function(oDescriptorInlineChange) {
		var that = this;
		return new Promise(function(resolve) {

			var fSetHostingIdForTextKey = function(_oDescriptorInlineChange, sId){
				//providing "hosting id" for appdescr_app_setTitle and similar
				//"hosting id" is descriptor variant id
				if ( _oDescriptorInlineChange["setHostingIdForTextKey"] ){
					_oDescriptorInlineChange.setHostingIdForTextKey(sId);
				}
			};

			switch (that._mode) {
				case 'NEW':
					fSetHostingIdForTextKey(oDescriptorInlineChange,that._id);
					that._content.push(oDescriptorInlineChange.getMap());
					break;
				case 'FROM_EXISTING':
					fSetHostingIdForTextKey(oDescriptorInlineChange,that._mMap.id);
					that._mMap.content.push(oDescriptorInlineChange.getMap());
					break;
				default:
					// do nothing
			}
			resolve(null);
		});
	};

	/**
	 * Set transport request (for ABAP Backend)
	 *
	 * @param {string} sTransportRequest ABAP transport request
	 *
	 * @return {Promise} resolving when setting of transport request was successful
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariant.prototype.setTransportRequest = function(sTransportRequest) {
		try {
			//partial check: length le 20, alphanumeric, upper case, no space not underscore - data element in ABAP: TRKORR, CHAR20
			Utils.checkTransportRequest(sTransportRequest);
		} catch (oError) {
			return Promise.reject(oError);
		}
		this._sTransportRequest = sTransportRequest;
		return Promise.resolve();
	};

	/**
	 * Set package (for ABAP Backend)
	 *
	 * @param {string} sPackage ABAP package
	 *
	 * @return {Promise} resolving when setting of package was successful
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariant.prototype.setPackage = function(sPackage) {
		try {
			//partial check: length le 30, alphanumeric, upper case, / for namespace, no space, no underscore - data element in ABAP: DEVCLASS, CHAR30
			Utils.checkPackage(sPackage);
		} catch (oError) {
			return Promise.reject(oError);
		}
		this._package = sPackage;
		return Promise.resolve();
	};

	/**
	 * Submits the descriptor variant to the backend
	 *
	 * @return {Promise} resolving when submitting the descriptor variant was successful
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariant.prototype.submit = function() {
		var sRoute = '/sap/bc/lrep/appdescr_variants/';
		var sMethod;

		switch (this._mode) {
			case 'NEW':
				sMethod = 'POST';
				break;
			case 'FROM_EXISTING':
				sMethod = 'PUT';
				sRoute = sRoute + this._getMap().id;
				break;
			case 'DELETION':
				sMethod = 'DELETE';
				sRoute = sRoute + this._id;
				break;
			default:
				// do nothing
		}

		var mMap = this._getMap();

		if (this._sTransportRequest) {
		//set to URL-Parameter 'changelist', as done in LrepConnector
			sRoute += '?changelist=' + this._sTransportRequest;
		} else if ( this._oSettings.isAtoEnabled() && mMap.layer == 'CUSTOMER' ) {
			sRoute += '?changelist=ATO_NOTIFICATION';
		}

		var oLREPConnector = new LrepConnector();

		return oLREPConnector.send(sRoute, sMethod, mMap);
	};

	DescriptorVariant.prototype._getMap = function() {
		switch (this._mode) {
			case 'NEW':
				var mResult = {
					"fileName": this._getNameAndNameSpace().fileName,
					"fileType": "appdescr_variant",
					"namespace": this._getNameAndNameSpace().namespace,
					"layer": this._layer,
					"packageName": this._package ? this._package : "$TMP",

					"reference": this._reference,
					"id": this._id,

					"content": this._content
				};
				if ( typeof this._isAppVariantRoot != undefined ) {
					mResult.isAppVariantRoot = this._isAppVariantRoot;
				}
				return mResult;

			case 'FROM_EXISTING':
			case 'DELETION':
				{
					return this._mMap;
				}
			default: // do nothing
		}
	};

	DescriptorVariant.prototype._getNameAndNameSpace = function() {
		return Utils.getNameAndNameSpace(this._id,this._reference);
	};

	/**
	 * Factory for Descriptor Variants
	 * @namespace
	 * @alias sap.ui.fl.descriptorRelated.api.DescriptorVariantFactory
	 * @author SAP SE
	 * @version ${version}
	 * @private
	 * @sap-restricted
	 */
	var DescriptorVariantFactory = {};

	DescriptorVariantFactory._getDescriptorVariant = function(sId) {
		var sRoute = '/sap/bc/lrep/appdescr_variants/' + sId;
		var oLREPConnector = new LrepConnector();
		return oLREPConnector.send(sRoute, 'GET');
	};

	/**
	 * Creates a new descriptor variant
	 *
	 * @param {object} mParameters
	 * @param {string} mParameters.reference the referenced descriptor or descriptor variant id
	 * @param {string} mParameters.id the id for the descriptor variant id
	 * @param {string} [mParameters.layer='CUSTOMER'] the layer for the descriptor variant
	 * @param {boolean} [mParameters.isAppVariantRoot=true] if descriptor variant is 'appVariantRoot'
	 *
	 * @return {Promise} resolving the new DescriptorVariant instance
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariantFactory.createNew = function(mParameters) {
		Utils.checkParameterAndType(mParameters, "reference", "string");
		Utils.checkParameterAndType(mParameters, "id", "string");

		//default layer to CUSTOMER
		if (!mParameters.layer){
			mParameters.layer = 'CUSTOMER';
		} else {
			Utils.checkParameterAndType(mParameters, "layer", "string");
			//TODO: is this necessary? already checked in Utils-method? -> checks only type
			if (mParameters.layer != 'VENDOR' && mParameters.layer != 'CUSTOMER') {
				//TODO: this should do a reject 	return Promise.reject(oError);
				throw new Error("Parameter \"layer\" needs to be 'VENDOR', 'CUSTOMER'");
			}
		}

		// isAppVariantRoot
		if (mParameters.isAppVariantRoot){
			Utils.checkParameterAndType(mParameters, "isAppVariantRoot", "boolean");
		}

		return Settings.getInstance().then(function(oSettings) {
			return Promise.resolve( new DescriptorVariant(mParameters,null,false,oSettings) );
		});
	};

	/**
	 * Creates a descriptor variant instance for an existing descriptor variant id
	 *
	 * @param {string} sId the id of the descriptor variant id
	 *
	 * @return {Promise} resolving the DescriptorVariant instance
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariantFactory.createForExisting = function(sId) {
		if (sId === undefined || typeof sId !== "string") {
			throw new Error("Parameter \"sId\" must be provided of type string");
		}

		var _mResult;
		return DescriptorVariantFactory._getDescriptorVariant(sId).then(function(mResult){
			_mResult = mResult;
			return Settings.getInstance();
		}).then( function(oSettings){
			var mDescriptorVariantJSON = JSON.parse(_mResult.response);
			return Promise.resolve(new DescriptorVariant(null,mDescriptorVariantJSON,false,oSettings));
		});
	};

	/**
	 * Creates a descriptor variant deletion
	 *
	 * @param {string} sId the id of the descriptor variant id
	 *
	 * @return {Promise} resolving the DescriptorVariant instance
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariantFactory.createDeletion = function(sId) {
		if (sId === undefined || typeof sId !== "string") {
			throw new Error("Parameter \"sId\" must be provided of type string");
		}
		var mParameter = {};
		mParameter.id = sId;

		var _mResult;
		return DescriptorVariantFactory._getDescriptorVariant(sId).then(function(mResult){
			_mResult = mResult;
			return Settings.getInstance();
		}).then( function(oSettings){
			var mDescriptorVariantJSON = JSON.parse(_mResult.response);
			return Promise.resolve(new DescriptorVariant(mParameter,mDescriptorVariantJSON,true,oSettings));
		});

	};

	return DescriptorVariantFactory;
},true);<commit_msg>[INTERNAL] Use LrepConnector.createConnector in DescriptorVariantFactory<commit_after>/*!
 * ${copyright}
 */

sap.ui.define([
	"sap/ui/fl/descriptorRelated/api/DescriptorInlineChangeFactory",
	"sap/ui/fl/Utils",
	"sap/ui/fl/LrepConnector",
	"sap/ui/fl/descriptorRelated/internal/Utils",
	"sap/ui/fl/descriptorRelated/api/Settings"
], function(DescriptorInlineChangeFactory, LrepUtils, LrepConnector, Utils, Settings) {
	"use strict";

	/**
	 * Descriptor Variant
	 *
	 * @param {object} mParameters parameters
	 * @param {string} mParameters.id the id of the descriptor variant id to be provided for a new descriptor variant and for deleting a descriptor variant
	 * @param {string} mParameters.reference the referenced descriptor or descriptor variant id to be provided when creating a new descriptor variant
	 * @param {boolean} [mParameters.isAppVariantRoot=true]
	 * @param {object} mFileContent file content of the existing descriptor variant to be provided if descriptor variant shall be created from an existing
	 * @param {boolean} [bDeletion=false] deletion indicator to be provided if descriptor variant shall be deleted
	 * @param {sap.ui.fl.descriptorRelated.api.Settings} oSettings settings
	 *
	 * @constructor
	 * @alias sap.ui.fl.descriptorRelated.api.DescriptorVariant
	 * @author SAP SE
	 * @version ${version}
	 * @private
	 * @sap-restricted
	 */

	//Descriptor Variant
	var DescriptorVariant = function(mParameters, mFileContent, bDeletion, oSettings) {
		if (mParameters && bDeletion) {
			this._id = mParameters.id;
			this._mode = 'DELETION';
			this._mMap = mFileContent;

		} else if (mParameters) {
			this._id = mParameters.id;
			this._reference = mParameters.reference;
			this._layer = mParameters.layer;
			if ( typeof mParameters.isAppVariantRoot != undefined){
				this._isAppVariantRoot = mParameters.isAppVariantRoot;
			}
			this._mode = 'NEW';

		} else if (mFileContent) {
			this._mMap = mFileContent;
			this._mode = 'FROM_EXISTING';

		}

		this._oSettings = oSettings;
		this._sTransportRequest = null;
		this._content = [];
	};

	/**
	 * Adds a descriptor inline change to the descriptor variant
	 *
	 * @param {sap.ui.fl.descriptorRelated.api.DescriptorInlineChange} oDescriptorInlineChange the inline change
	 *
	 * @return {Promise} resolving when adding the descriptor inline change was successful (without backend access)
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariant.prototype.addDescriptorInlineChange = function(oDescriptorInlineChange) {
		var that = this;
		return new Promise(function(resolve) {

			var fSetHostingIdForTextKey = function(_oDescriptorInlineChange, sId){
				//providing "hosting id" for appdescr_app_setTitle and similar
				//"hosting id" is descriptor variant id
				if ( _oDescriptorInlineChange["setHostingIdForTextKey"] ){
					_oDescriptorInlineChange.setHostingIdForTextKey(sId);
				}
			};

			switch (that._mode) {
				case 'NEW':
					fSetHostingIdForTextKey(oDescriptorInlineChange,that._id);
					that._content.push(oDescriptorInlineChange.getMap());
					break;
				case 'FROM_EXISTING':
					fSetHostingIdForTextKey(oDescriptorInlineChange,that._mMap.id);
					that._mMap.content.push(oDescriptorInlineChange.getMap());
					break;
				default:
					// do nothing
			}
			resolve(null);
		});
	};

	/**
	 * Set transport request (for ABAP Backend)
	 *
	 * @param {string} sTransportRequest ABAP transport request
	 *
	 * @return {Promise} resolving when setting of transport request was successful
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariant.prototype.setTransportRequest = function(sTransportRequest) {
		try {
			//partial check: length le 20, alphanumeric, upper case, no space not underscore - data element in ABAP: TRKORR, CHAR20
			Utils.checkTransportRequest(sTransportRequest);
		} catch (oError) {
			return Promise.reject(oError);
		}
		this._sTransportRequest = sTransportRequest;
		return Promise.resolve();
	};

	/**
	 * Set package (for ABAP Backend)
	 *
	 * @param {string} sPackage ABAP package
	 *
	 * @return {Promise} resolving when setting of package was successful
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariant.prototype.setPackage = function(sPackage) {
		try {
			//partial check: length le 30, alphanumeric, upper case, / for namespace, no space, no underscore - data element in ABAP: DEVCLASS, CHAR30
			Utils.checkPackage(sPackage);
		} catch (oError) {
			return Promise.reject(oError);
		}
		this._package = sPackage;
		return Promise.resolve();
	};

	/**
	 * Submits the descriptor variant to the backend
	 *
	 * @return {Promise} resolving when submitting the descriptor variant was successful
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariant.prototype.submit = function() {
		var sRoute = '/sap/bc/lrep/appdescr_variants/';
		var sMethod;

		switch (this._mode) {
			case 'NEW':
				sMethod = 'POST';
				break;
			case 'FROM_EXISTING':
				sMethod = 'PUT';
				sRoute = sRoute + this._getMap().id;
				break;
			case 'DELETION':
				sMethod = 'DELETE';
				sRoute = sRoute + this._id;
				break;
			default:
				// do nothing
		}

		var mMap = this._getMap();

		if (this._sTransportRequest) {
		//set to URL-Parameter 'changelist', as done in LrepConnector
			sRoute += '?changelist=' + this._sTransportRequest;
		} else if ( this._oSettings.isAtoEnabled() && mMap.layer == 'CUSTOMER' ) {
			sRoute += '?changelist=ATO_NOTIFICATION';
		}

		var oLREPConnector = LrepConnector.createConnector();

		return oLREPConnector.send(sRoute, sMethod, mMap);
	};

	DescriptorVariant.prototype._getMap = function() {
		switch (this._mode) {
			case 'NEW':
				var mResult = {
					"fileName": this._getNameAndNameSpace().fileName,
					"fileType": "appdescr_variant",
					"namespace": this._getNameAndNameSpace().namespace,
					"layer": this._layer,
					"packageName": this._package ? this._package : "$TMP",

					"reference": this._reference,
					"id": this._id,

					"content": this._content
				};
				if ( typeof this._isAppVariantRoot != undefined ) {
					mResult.isAppVariantRoot = this._isAppVariantRoot;
				}
				return mResult;

			case 'FROM_EXISTING':
			case 'DELETION':
				{
					return this._mMap;
				}
			default: // do nothing
		}
	};

	DescriptorVariant.prototype._getNameAndNameSpace = function() {
		return Utils.getNameAndNameSpace(this._id,this._reference);
	};

	/**
	 * Factory for Descriptor Variants
	 * @namespace
	 * @alias sap.ui.fl.descriptorRelated.api.DescriptorVariantFactory
	 * @author SAP SE
	 * @version ${version}
	 * @private
	 * @sap-restricted
	 */
	var DescriptorVariantFactory = {};

	DescriptorVariantFactory._getDescriptorVariant = function(sId) {
		var sRoute = '/sap/bc/lrep/appdescr_variants/' + sId;
		var oLREPConnector = LrepConnector.createConnector();
		return oLREPConnector.send(sRoute, 'GET');
	};

	/**
	 * Creates a new descriptor variant
	 *
	 * @param {object} mParameters
	 * @param {string} mParameters.reference the referenced descriptor or descriptor variant id
	 * @param {string} mParameters.id the id for the descriptor variant id
	 * @param {string} [mParameters.layer='CUSTOMER'] the layer for the descriptor variant
	 * @param {boolean} [mParameters.isAppVariantRoot=true] if descriptor variant is 'appVariantRoot'
	 *
	 * @return {Promise} resolving the new DescriptorVariant instance
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariantFactory.createNew = function(mParameters) {
		Utils.checkParameterAndType(mParameters, "reference", "string");
		Utils.checkParameterAndType(mParameters, "id", "string");

		//default layer to CUSTOMER
		if (!mParameters.layer){
			mParameters.layer = 'CUSTOMER';
		} else {
			Utils.checkParameterAndType(mParameters, "layer", "string");
			//TODO: is this necessary? already checked in Utils-method? -> checks only type
			if (mParameters.layer != 'VENDOR' && mParameters.layer != 'CUSTOMER') {
				//TODO: this should do a reject 	return Promise.reject(oError);
				throw new Error("Parameter \"layer\" needs to be 'VENDOR', 'CUSTOMER'");
			}
		}

		// isAppVariantRoot
		if (mParameters.isAppVariantRoot){
			Utils.checkParameterAndType(mParameters, "isAppVariantRoot", "boolean");
		}

		return Settings.getInstance().then(function(oSettings) {
			return Promise.resolve( new DescriptorVariant(mParameters,null,false,oSettings) );
		});
	};

	/**
	 * Creates a descriptor variant instance for an existing descriptor variant id
	 *
	 * @param {string} sId the id of the descriptor variant id
	 *
	 * @return {Promise} resolving the DescriptorVariant instance
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariantFactory.createForExisting = function(sId) {
		if (sId === undefined || typeof sId !== "string") {
			throw new Error("Parameter \"sId\" must be provided of type string");
		}

		var _mResult;
		return DescriptorVariantFactory._getDescriptorVariant(sId).then(function(mResult){
			_mResult = mResult;
			return Settings.getInstance();
		}).then( function(oSettings){
			var mDescriptorVariantJSON = JSON.parse(_mResult.response);
			return Promise.resolve(new DescriptorVariant(null,mDescriptorVariantJSON,false,oSettings));
		});
	};

	/**
	 * Creates a descriptor variant deletion
	 *
	 * @param {string} sId the id of the descriptor variant id
	 *
	 * @return {Promise} resolving the DescriptorVariant instance
	 *
	 * @private
	 * @sap-restricted
	 */
	DescriptorVariantFactory.createDeletion = function(sId) {
		if (sId === undefined || typeof sId !== "string") {
			throw new Error("Parameter \"sId\" must be provided of type string");
		}
		var mParameter = {};
		mParameter.id = sId;

		var _mResult;
		return DescriptorVariantFactory._getDescriptorVariant(sId).then(function(mResult){
			_mResult = mResult;
			return Settings.getInstance();
		}).then( function(oSettings){
			var mDescriptorVariantJSON = JSON.parse(_mResult.response);
			return Promise.resolve(new DescriptorVariant(mParameter,mDescriptorVariantJSON,true,oSettings));
		});

	};

	return DescriptorVariantFactory;
},true);<|endoftext|>


<commit_before>sap.ui.define([
	"sap/ui/rta/RuntimeAuthoring",
	"sap/ui/rta/plugin/Plugin",
	"sap/ui/fl/Utils",
	"sap/ui/fl/registry/Settings",
	"sap/ui/fl/write/api/PersistenceWriteAPI",
	"sap/ui/thirdparty/sinon-4"
], function(
	RuntimeAuthoring,
	Plugin,
	FlexUtils,
	FlexSettings,
	PersistenceWriteAPI,
	sinon
) {
	"use strict";

	var sandbox = sinon.sandbox.create();
	var oManifest = {
		"sap.app" : {
			id : "sap.ui.rta.test",
			applicationVersion : {
				version : "1.2.3"
			}
		}
	};

	var oMockedAppComponent = {
		getManifestEntry: function () {
			return {};
		},
		getMetadata: function () {
			return {
				getName: function () {
					return "someName";
				},
				getManifest: function () {
					return oManifest;
				}
			};
		},
		getManifest: function () {
			return oManifest;
		},
		getModel: function () {},
		getLocalId: function() {},
		getComponentData: function () {}
	};

	var Util = {
		_createStartRtaFunction: function(oRuntimeAuthoring) {
			return function() {
				return Promise.all([
					new Promise(function (fnResolve) {
						oRuntimeAuthoring.attachStart(fnResolve);
					}),
					oRuntimeAuthoring.start()
				]);
			};
		},

		_defineTestStubs: function() {
			sandbox.stub(FlexSettings, "getInstance").returns(Promise.resolve({
				isProductiveSystem: function() { return false; },
				isVersioningEnabled: function () { return false; }
			}));
			sandbox.stub(FlexUtils, "getAppComponentForControl").returns(oMockedAppComponent);
			sandbox.stub(PersistenceWriteAPI, "getResetAndPublishInfo").resolves({
				isResetEnabled: true,
				isPublishEnabled: false
			});
		},

		startRta: function(oHorizontalLayout, aPlugins) {
			var oRuntimeAuthoring = new RuntimeAuthoring({
				rootControl: oHorizontalLayout,
				showToolbars: false
			});
			if (aPlugins && Array.isArray(aPlugins)) {
				if (!aPlugins.length) {
					aPlugins.push(new Plugin("abc"));
				}
				oRuntimeAuthoring.setPlugins(aPlugins);
			}
			Util._defineTestStubs(oRuntimeAuthoring);

			// will result in custom timer in webPageTest
			window.performance.mark("rta.start.starts");

			return oRuntimeAuthoring.start()
			.then(function() {
				var sMeasureName = "RTA start function called";
				//will result in custom timer in webPageTest
				window.performance.mark("rta.start.ends");
				window.performance.measure(sMeasureName, "rta.start.starts", "rta.start.ends");
				sap.ui.rta.startTime = window.performance.getEntriesByName(sMeasureName)[0].duration;
				jQuery.sap.log.info(sMeasureName, sap.ui.rta.startTime + "ms");
				//visual change at the end
				var oOverlay = sap.ui.dt.OverlayRegistry.getOverlay(oHorizontalLayout);
				oOverlay.setSelected(true);
			})
			.then(function() {
				sandbox.restore();
			});
		},

		startRtaWithoutStretch: function(oRootControl) {
			var oRuntimeAuthoring = new RuntimeAuthoring({
				rootControl: oRootControl
			});
			var mPlugins = oRuntimeAuthoring.getDefaultPlugins();
			delete mPlugins["stretch"];
			oRuntimeAuthoring.setPlugins(mPlugins);

			Util._defineTestStubs(oRuntimeAuthoring);

			// will result in custom timer in webPageTest
			window.performance.mark("rta.start.starts");

			return oRuntimeAuthoring.start()
			.then(function() {
				var sMeasureName = "RTA start function called";
				//will result in custom timer in webPageTest
				window.performance.mark("rta.start.ends");
				window.performance.measure(sMeasureName, "rta.start.starts", "rta.start.ends");
				sap.ui.rta.startTime = window.performance.getEntriesByName(sMeasureName)[0].duration;
				jQuery.sap.log.info(sMeasureName, sap.ui.rta.startTime + "ms");
				//visual change at the end
				var oOverlay = sap.ui.dt.OverlayRegistry.getOverlay(oRootControl);
				oOverlay.setSelected(true);
			})
			.then(function() {
				sandbox.restore();
			});
		},

		startRtaConstructorOnly: function(oHorizontalLayout) {
			var iRtaStartCounter = 1000;
			var sMeasureName = "RTA init function called " + iRtaStartCounter + " times";
			window.performance.clearMeasures();

			// will result in custom timer in webPageTest
			window.performance.mark("rta.init.starts");

			for (var i = 0; i < iRtaStartCounter; i++) {
				/* eslint no-new: 0 */
				new RuntimeAuthoring({
					rootControl : oHorizontalLayout
				});
			}

			//will result in custom timer in webPageTest
			window.performance.mark("rta.init.ends");
			window.performance.measure(sMeasureName, "rta.init.starts", "rta.init.ends");
			sap.ui.rta.creationTime = window.performance.getEntriesByName(sMeasureName)[0].duration;
			jQuery.sap.log.info(sMeasureName, sap.ui.rta.creationTime + "ms");
		}
	};

	window.startRta = Util.startRta;
	window.startRtaConstructorOnly = Util.startRtaConstructorOnly;

	return Util;
}, true);
<commit_msg>[INTERNAL][FIX] sap.ui.rta - rta load without stretch perf test fixed<commit_after>sap.ui.define([
	"sap/ui/rta/RuntimeAuthoring",
	"sap/ui/rta/plugin/Plugin",
	"sap/ui/fl/Utils",
	"sap/ui/fl/registry/Settings",
	"sap/ui/fl/write/api/PersistenceWriteAPI",
	"sap/ui/thirdparty/sinon-4"
], function(
	RuntimeAuthoring,
	Plugin,
	FlexUtils,
	FlexSettings,
	PersistenceWriteAPI,
	sinon
) {
	"use strict";

	var sandbox = sinon.sandbox.create();
	var oManifest = {
		"sap.app" : {
			id : "sap.ui.rta.test",
			applicationVersion : {
				version : "1.2.3"
			}
		}
	};

	var oMockedAppComponent = {
		getManifestEntry: function () {
			return {};
		},
		getMetadata: function () {
			return {
				getName: function () {
					return "someName";
				},
				getManifest: function () {
					return oManifest;
				}
			};
		},
		getManifest: function () {
			return oManifest;
		},
		getModel: function () {},
		getLocalId: function() {},
		getComponentData: function () {}
	};

	var Util = {
		_createStartRtaFunction: function(oRuntimeAuthoring) {
			return function() {
				return Promise.all([
					new Promise(function (fnResolve) {
						oRuntimeAuthoring.attachStart(fnResolve);
					}),
					oRuntimeAuthoring.start()
				]);
			};
		},

		_defineTestStubs: function() {
			sandbox.stub(FlexSettings, "getInstance").returns(Promise.resolve({
				isProductiveSystem: function () { return false; },
				isVersioningEnabled: function () { return false; },
				isAppVariantSaveAsEnabled: function () { return false; }
			}));
			sandbox.stub(FlexUtils, "getAppComponentForControl").returns(oMockedAppComponent);
			sandbox.stub(PersistenceWriteAPI, "getResetAndPublishInfo").resolves({
				isResetEnabled: true,
				isPublishEnabled: false
			});
		},

		startRta: function(oHorizontalLayout, aPlugins) {
			var oRuntimeAuthoring = new RuntimeAuthoring({
				rootControl: oHorizontalLayout,
				showToolbars: false
			});
			if (aPlugins && Array.isArray(aPlugins)) {
				if (!aPlugins.length) {
					aPlugins.push(new Plugin("abc"));
				}
				oRuntimeAuthoring.setPlugins(aPlugins);
			}
			Util._defineTestStubs(oRuntimeAuthoring);

			// will result in custom timer in webPageTest
			window.performance.mark("rta.start.starts");

			return oRuntimeAuthoring.start()
			.then(function() {
				var sMeasureName = "RTA start function called";
				//will result in custom timer in webPageTest
				window.performance.mark("rta.start.ends");
				window.performance.measure(sMeasureName, "rta.start.starts", "rta.start.ends");
				sap.ui.rta.startTime = window.performance.getEntriesByName(sMeasureName)[0].duration;
				jQuery.sap.log.info(sMeasureName, sap.ui.rta.startTime + "ms");
				//visual change at the end
				var oOverlay = sap.ui.dt.OverlayRegistry.getOverlay(oHorizontalLayout);
				oOverlay.setSelected(true);
			})
			.then(function() {
				sandbox.restore();
			});
		},

		startRtaWithoutStretch: function(oRootControl) {
			var oRuntimeAuthoring = new RuntimeAuthoring({
				rootControl: oRootControl
			});
			var mPlugins = oRuntimeAuthoring.getDefaultPlugins();
			delete mPlugins["stretch"];
			oRuntimeAuthoring.setPlugins(mPlugins);

			Util._defineTestStubs(oRuntimeAuthoring);

			// will result in custom timer in webPageTest
			window.performance.mark("rta.start.starts");

			return oRuntimeAuthoring.start()
			.then(function() {
				var sMeasureName = "RTA start function called";
				//will result in custom timer in webPageTest
				window.performance.mark("rta.start.ends");
				window.performance.measure(sMeasureName, "rta.start.starts", "rta.start.ends");
				sap.ui.rta.startTime = window.performance.getEntriesByName(sMeasureName)[0].duration;
				jQuery.sap.log.info(sMeasureName, sap.ui.rta.startTime + "ms");
				//visual change at the end
				var oOverlay = sap.ui.dt.OverlayRegistry.getOverlay(oRootControl);
				oOverlay.setSelected(true);
			})
			.then(function() {
				sandbox.restore();
			});
		},

		startRtaConstructorOnly: function(oHorizontalLayout) {
			var iRtaStartCounter = 1000;
			var sMeasureName = "RTA init function called " + iRtaStartCounter + " times";
			window.performance.clearMeasures();

			// will result in custom timer in webPageTest
			window.performance.mark("rta.init.starts");

			for (var i = 0; i < iRtaStartCounter; i++) {
				/* eslint no-new: 0 */
				new RuntimeAuthoring({
					rootControl : oHorizontalLayout
				});
			}

			//will result in custom timer in webPageTest
			window.performance.mark("rta.init.ends");
			window.performance.measure(sMeasureName, "rta.init.starts", "rta.init.ends");
			sap.ui.rta.creationTime = window.performance.getEntriesByName(sMeasureName)[0].duration;
			jQuery.sap.log.info(sMeasureName, sap.ui.rta.creationTime + "ms");
		}
	};

	window.startRta = Util.startRta;
	window.startRtaConstructorOnly = Util.startRtaConstructorOnly;

	return Util;
}, true);
<|endoftext|>


<commit_before>/**
 * Created by ksinger on 15.07.2016.
 */

import won from '../won-es6';
import angular from 'angular';
import Immutable from 'immutable'; // also exports itself as (window).L
import 'leaflet';
import 'ng-redux';
//import { labels } from '../won-label-utils';
import { attach, searchNominatim } from '../utils.js';
import { actionCreators }  from '../actions/actions';
import { } from '../selectors';
import { doneTypingBufferNg } from '../cstm-ng-utils'

const serviceDependencies = ['$scope', '$ngRedux', '$element'];
function genComponentConf() {
    //TODO input as text-input or contenteditable? need to overl
    let template = `

        <input type="text" class="lp__searchbox" placeholder="Search for location"/>

                    {{ result.name }}

            `;

    class Controller {
        constructor() {
            attach(this, serviceDependencies, arguments);

            this.initMap();

            window.lp4dbg = this;
            const selectFromState = (state)=>{
                return {
                };
            }

            doneTypingBufferNg(
                e => this.doneTyping(e),
                this.textfieldNg(), 1000
            )

            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);

        }
        initMap() {
            // Leaflet + JS-Bundling fix:
            L.Icon.Default.imagePath = 'images/map-images/';
            //TODO replace with own icons

            const secureOsmSource = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' // secure osm.org
            const secureOsm = L.tileLayer(secureOsmSource, {
                attribution: '© OpenStreetMap contributors'
            });

            const transportSource = 'http://{s}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png';
            const transport = L.tileLayer(transportSource, {
                attribution: 'Maps © Thunderforest, Data © OpenStreetMap contributors',
            });

            this.map = L.map('lp__mapmount',{
                center: [51.505, -0.09],
                zoom: 13,
                layers: [secureOsm], //layers enabled from beginning on

            }); //.setView([51.505, -0.09], 13);

            const baseMaps = {
                "Default": secureOsm,
                "Transport (Insecure)": transport,
            }

            L.control.layers(baseMaps).addTo(this.map);

            L.marker([51.5, -0.09]).addTo(this.map)
                .bindPopup('A pretty CSS3 popup.
 Easily customizable.')
                .openPopup();

            // Force it to adapt to actual size
            // for some reason this doesn't happen by default
            // when the map is within a tag.
            // this.map.invalidateSize();
            // ^ doesn't work (needs to be done manually atm);

        }
        doneTyping() {
            console.log('starting type-ahead search for: ' + this.textfield().value);
            //buffer for 1s before starting the search
            searchNominatim(this.textfield().value)
            .then( searchResults => {
                console.log('location search results: ', searchResults);
                this.$scope.$apply(() =>
                    //this.searchResults = searchResults.map(nominatim2wonLocation)

                    this.searchResults = scrubSearchResults(searchResults)
                )
            })

        }
        clickedSearchResult(location) {
            console.log('selected location: ', location)

        }

        textfieldNg() {
            if(!this._textfield) {
                this._textfield = this.$element.find('.lp__searchbox');
            }
            return this._textfield;
        }

        textfield() {
            return this.textfieldNg()[0];
        }

        mapMountNg() {
            if(!this._mapMount) {
                this._mapMount = this.$element.find('.lp__mapmount');
            }
            return this._mapMount;
        }

        mapMount() {
            return this.mapMountNg()[0];
        }
    }
    Controller.$inject = serviceDependencies;

    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        scope: {
        },
        template: template
    }
}

function scrubSearchResults(searchResults) {

    return Immutable.fromJS(
            searchResults.map(nominatim2wonLocation)
        )
        /*
         * filter "duplicate" results (e.g. "Wien"
         *  -> 1x waterway, 1x boundary, 1x place)
         */
        .groupBy(r => r.get('name'))
        .map(sameNamedResults => sameNamedResults.first())
        .toList()
        .toJS()
}

/**
 * drop info not stored in rdf, thus info that we
 * couldn't restore for previously used locations
 */
function nominatim2wonLocation(searchResult) {
    return {
        name: searchResult.display_name,
        lon: searchResult.lon,
        lat: searchResult.lat,
        //importance: searchResult.importance,
        boundingbox: searchResult.boundingbox, // TODO use this to set proper zoom
    }
}

export default angular.module('won.owner.components.locationPicker', [
    ])
    .directive('wonLocationPicker', genComponentConf)
    .name;

window.searchNominatim4dbg = searchNominatim;<commit_msg>More efficient dom-lookup during map initialisation.<commit_after>/**
 * Created by ksinger on 15.07.2016.
 */

import won from '../won-es6';
import angular from 'angular';
import Immutable from 'immutable'; // also exports itself as (window).L
import 'leaflet';
import 'ng-redux';
//import { labels } from '../won-label-utils';
import { attach, searchNominatim } from '../utils.js';
import { actionCreators }  from '../actions/actions';
import { } from '../selectors';
import { doneTypingBufferNg } from '../cstm-ng-utils'

const serviceDependencies = ['$scope', '$ngRedux', '$element'];
function genComponentConf() {
    //TODO input as text-input or contenteditable? need to overl
    let template = `

        <input type="text" class="lp__searchbox" placeholder="Search for location"/>

                    {{ result.name }}

            `;

    class Controller {
        constructor() {
            attach(this, serviceDependencies, arguments);

            this.initMap();

            window.lp4dbg = this;
            const selectFromState = (state)=>{
                return {
                };
            }

            doneTypingBufferNg(
                e => this.doneTyping(e),
                this.textfieldNg(), 1000
            )

            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);

        }
        initMap() {
            // Leaflet + JS-Bundling fix:
            L.Icon.Default.imagePath = 'images/map-images/';
            //TODO replace with own icons

            const secureOsmSource = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' // secure osm.org
            const secureOsm = L.tileLayer(secureOsmSource, {
                attribution: '© OpenStreetMap contributors'
            });

            const transportSource = 'http://{s}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png';
            const transport = L.tileLayer(transportSource, {
                attribution: 'Maps © Thunderforest, Data © OpenStreetMap contributors',
            });

            this.map = L.map(this.mapMount(),{
                center: [51.505, -0.09],
                zoom: 13,
                layers: [secureOsm], //initially visible layers

            }); //.setView([51.505, -0.09], 13);

            const baseMaps = {
                "Default": secureOsm,
                "Transport (Insecure)": transport,
            }

            L.control.layers(baseMaps).addTo(this.map);

            L.marker([51.5, -0.09]).addTo(this.map)
                .bindPopup('A pretty CSS3 popup.
 Easily customizable.')
                .openPopup();

            // Force it to adapt to actual size
            // for some reason this doesn't happen by default
            // when the map is within a tag.
            // this.map.invalidateSize();
            // ^ doesn't work (needs to be done manually atm);

        }
        doneTyping() {
            console.log('starting type-ahead search for: ' + this.textfield().value);
            //buffer for 1s before starting the search
            searchNominatim(this.textfield().value)
            .then( searchResults => {
                console.log('location search results: ', searchResults);
                this.$scope.$apply(() =>
                    //this.searchResults = searchResults.map(nominatim2wonLocation)

                    this.searchResults = scrubSearchResults(searchResults)
                )
            })

        }
        clickedSearchResult(location) {
            console.log('selected location: ', location)

        }

        textfieldNg() {
            if(!this._textfield) {
                this._textfield = this.$element.find('.lp__searchbox');
            }
            return this._textfield;
        }

        textfield() {
            return this.textfieldNg()[0];
        }

        mapMountNg() {
            if(!this._mapMount) {
                this._mapMount = this.$element.find('.lp__mapmount');
            }
            return this._mapMount;
        }

        mapMount() {
            return this.mapMountNg()[0];
        }
    }
    Controller.$inject = serviceDependencies;

    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        scope: {
        },
        template: template
    }
}

function scrubSearchResults(searchResults) {

    return Immutable.fromJS(
            searchResults.map(nominatim2wonLocation)
        )
        /*
         * filter "duplicate" results (e.g. "Wien"
         *  -> 1x waterway, 1x boundary, 1x place)
         */
        .groupBy(r => r.get('name'))
        .map(sameNamedResults => sameNamedResults.first())
        .toList()
        .toJS()
}

/**
 * drop info not stored in rdf, thus info that we
 * couldn't restore for previously used locations
 */
function nominatim2wonLocation(searchResult) {
    return {
        name: searchResult.display_name,
        lon: searchResult.lon,
        lat: searchResult.lat,
        //importance: searchResult.importance,
        boundingbox: searchResult.boundingbox, // TODO use this to set proper zoom
    }
}

export default angular.module('won.owner.components.locationPicker', [
    ])
    .directive('wonLocationPicker', genComponentConf)
    .name;

window.searchNominatim4dbg = searchNominatim;<|endoftext|>


<commit_before>import { expect } from 'chai';
import React from 'react';
import NumericInput from 'react-numeric-input';
import simple from 'simple-mock';

import DatePicker from 'shared/date-picker';
import { shallowWithIntl } from 'utils/testUtils';
import SelectControl from 'pages/search/controls/SelectControl';
import RecurringReservationControls from './RecurringReservationControls';

function getWrapper(props) {
  const defaults = {
    changeFrequency: () => null,
    changeNumberOfOccurrences: () => null,
    changeLastTime: () => null,
    frequency: 'days',
    frequencyOptions: [{ label: '', value: '' }],
    isVisible: true,
    lastTime: null,
    numberOfOccurrences: 1,
  };
  return shallowWithIntl(<RecurringReservationControls {...defaults} {...props} />);
}

describe('shared/RecurringReservationControls/RecurringReservationControls', () => {
  it('renders an empty span if isVisible is false', () => {
    const wrapper = getWrapper({ isVisible: false });
    expect(wrapper.equals(<span />)).to.be.true;
  });

  it('renders Select with correct props', () => {
    const props = {
      changeFrequency: () => null,
      frequency: 'days',
      frequencyOptions: [{ label: '', value: '' }],
      lastTime: '2017-04-09',
    };
    const select = getWrapper(props).find(SelectControl);
    expect(select).to.have.length(1);
    expect(select.prop('onChange')).to.equal(props.changeFrequency);
    expect(select.prop('options')).to.equal(props.frequencyOptions);
    expect(select.prop('value')).to.equal(props.frequency);
  });

  it('renders NumericInput to change number of occurrences', () => {
    const props = {
      changeNumberOfOccurrences: () => null,
      lastTime: '2017-04-09',
      numberOfOccurrences: 12,
    };
    const control = getWrapper(props).find(NumericInput);
    expect(control).to.have.length(1);
    expect(control.prop('min')).to.equal(1);
    expect(control.prop('value')).to.equal(props.numberOfOccurrences);
    expect(control.prop('onChange')).to.equal(props.changeNumberOfOccurrences);
  });

  it('renders DatePicker with correct props', () => {
    const props = {
      lastTime: '2017-04-09',
      numberOfOccurrences: 12,
      changeLastTime: simple.mock(),
    };
    const control = getWrapper(props).find(DatePicker);
    expect(control).to.have.length(1);
    expect(control.prop('dateFormat')).to.equal('D.M.YYYY');
    expect(control.prop('formControl')).to.be.true;
    expect(control.prop('onChange')).to.equal(props.changeLastTime);
    expect(control.prop('value')).to.equal(props.lastTime);
  });

  describe('without set frecuency', () => {
    let wrapper;
    before(() => {
      wrapper = getWrapper({ frequency: '' });
    });

    it('does not render numbrerOfOcurrences NumericInput', () => {
      expect(wrapper.find(NumericInput)).to.have.length(0);
    });

    it('does not render DatePicker', () => {
      expect(wrapper.find(DatePicker)).to.have.length(0);
    });
  });
});
<commit_msg>add unit test for translation<commit_after>import { expect } from 'chai';
import React from 'react';
import NumericInput from 'react-numeric-input';
import simple from 'simple-mock';

import DatePicker from 'shared/date-picker';
import { shallowWithIntl } from 'utils/testUtils';
import SelectControl from 'pages/search/controls/SelectControl';
import RecurringReservationControls from './RecurringReservationControls';

function getWrapper(props) {
  const defaults = {
    changeFrequency: () => null,
    changeNumberOfOccurrences: () => null,
    changeLastTime: () => null,
    frequency: 'days',
    frequencyOptions: [{ label: '', value: '' }],
    isVisible: true,
    lastTime: null,
    numberOfOccurrences: 1,
  };
  return shallowWithIntl(<RecurringReservationControls {...defaults} {...props} />);
}

describe('shared/RecurringReservationControls/RecurringReservationControls', () => {
  it('renders an empty span if isVisible is false', () => {
    const wrapper = getWrapper({ isVisible: false });
    expect(wrapper.equals(<span />)).to.be.true;
  });

  it('renders Select with correct props', () => {
    const props = {
      changeFrequency: () => null,
      frequency: 'days',
      frequencyOptions: [{ label: '', value: '' }],
      lastTime: '2017-04-09',
    };
    const select = getWrapper(props).find(SelectControl);
    expect(select).to.have.length(1);
    expect(select.prop('onChange')).to.equal(props.changeFrequency);
    expect(select.prop('options')).to.equal(props.frequencyOptions);
    expect(select.prop('value')).to.equal(props.frequency);
  });

  it('renders NumericInput to change number of occurrences', () => {
    const props = {
      changeNumberOfOccurrences: () => null,
      lastTime: '2017-04-09',
      numberOfOccurrences: 12,
    };
    const control = getWrapper(props).find(NumericInput);
    expect(control).to.have.length(1);
    expect(control.prop('min')).to.equal(1);
    expect(control.prop('value')).to.equal(props.numberOfOccurrences);
    expect(control.prop('onChange')).to.equal(props.changeNumberOfOccurrences);
  });

  it('renders DatePicker with correct props', () => {
    const props = {
      lastTime: '2017-04-09',
      numberOfOccurrences: 12,
      changeLastTime: simple.mock(),
    };
    const control = getWrapper(props).find(DatePicker);
    expect(control).to.have.length(1);
    expect(control.prop('dateFormat')).to.equal('D.M.YYYY');
    expect(control.prop('formControl')).to.be.true;
    expect(control.prop('onChange')).to.equal(props.changeLastTime);
    expect(control.prop('value')).to.equal(props.lastTime);
  });

  describe('without set frecuency', () => {
    let wrapper;
    before(() => {
      wrapper = getWrapper({ frequency: '' });
    });

    it('does not render numbrerOfOcurrences NumericInput', () => {
      expect(wrapper.find(NumericInput)).to.have.length(0);
    });

    it('does not render DatePicker', () => {
      expect(wrapper.find(DatePicker)).to.have.length(0);
    });
  });

  describe.only('has recurring select option', () => {
    it('translated', () => {
      const arg = 'foo';
      const tMock = simple.mock();

      const wrapper = getWrapper({ frequency: '' });
      wrapper.setProps({ t: tMock });
      const getOptionLabel = wrapper.find(SelectControl).prop('getOptionLabel');
      getOptionLabel({ label: arg });

      expect(tMock.lastCall.arg).to.equal(arg);
    });
  });
});
<|endoftext|>