<commit_before>import { now } from '../../core/util';
import { on, off, getEventContainerPoint, preventDefault, stopPropagation } from '../../core/util/dom';
import Handler from '../../handler/Handler';
import Geometry from '../../geometry/Geometry';
import Map from '../Map';

const EVENTS =
    /**
     * mousedown event
     * @event Geometry#mousedown
     * @type {Object}
     * @property {String} type                    - mousedown
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'mousedown ' +
    /**
     * mouseup event
     * @event Geometry#mouseup
     * @type {Object}
     * @property {String} type                    - mouseup
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'mouseup ' +
    /**
     * mousemove event
     * @event Geometry#mousemove
     * @type {Object}
     * @property {String} type                    - mousemove
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'mousemove ' +
    /**
     * click event
     * @event Geometry#click
     * @type {Object}
     * @property {String} type                    - click
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'click ' +
    /**
     * dblclick event
     * @event Geometry#dblclick
     * @type {Object}
     * @property {String} type                    - dblclick
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'dblclick ' +
    /**
     * contextmenu event
     * @event Geometry#contextmenu
     * @type {Object}
     * @property {String} type                    - contextmenu
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'contextmenu ' +
    /**
     * touchstart event
     * @event Geometry#touchstart
     * @type {Object}
     * @property {String} type                    - touchstart
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'touchstart ' +
    /**
     * touchmove event
     * @event Geometry#touchmove
     * @type {Object}
     * @property {String} type                    - touchmove
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'touchmove ' +
    /**
     * touchend event
     * @event Geometry#touchend
     * @type {Object}
     * @property {String} type                    - touchend
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'touchend';
    /**
     * mouseenter event for geometry
     * @event Geometry#mouseenter
     * @type {Object}
     * @property {String} type                    - mouseenter
     * @property {Geometry} target       - the geometry fires mouseenter
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    /**
     * mouseover event for geometry
     * @event Geometry#mouseover
     * @type {Object}
     * @property {String} type                    - mouseover
     * @property {Geometry} target       - the geometry fires mouseover
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    /**
     * mouseout event for geometry
     * @event Geometry#mouseout
     * @type {Object}
     * @property {String} type                    - mouseout
     * @property {Geometry} target       - the geometry fires mouseout
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */

class MapGeometryEventsHandler extends Handler {

    addHooks() {
        const map = this.target;
        const dom = map._panels.allLayers || map._containerDOM;
        on(dom, EVENTS, this._identifyGeometryEvents, this);
    }

    removeHooks() {
        const map = this.target;
        const dom = map._panels.allLayers || map._containerDOM;
        off(dom, EVENTS, this._identifyGeometryEvents, this);
    }

    _identifyGeometryEvents(domEvent, type) {
        const map = this.target;
        if (map.isInteracting() || map._ignoreEvent(domEvent)) {
            return;
        }
        let oneMoreEvent = null;
        const eventType = type || domEvent.type;
        // ignore click lasted for more than 300ms.
        const isMousedown = eventType === 'mousedown' || (eventType === 'touchstart' && domEvent.touches && domEvent.touches.length === 1);
        if (isMousedown) {
            this._mouseDownTime = now();
        } else if ((eventType === 'click' || eventType === 'touchend') && this._mouseDownTime) {
            const downTime = this._mouseDownTime;
            delete this._mouseDownTime;
            const time = now();
            if (time - downTime > 300) {
                if (eventType === 'click') {
                    return;
                }
            } else if (eventType === 'touchend') {
                oneMoreEvent = 'click';
            }
        }

        const actual = domEvent.touches && domEvent.touches.length > 0 ?
            domEvent.touches[0] : domEvent.changedTouches && domEvent.changedTouches.length > 0 ?
                domEvent.changedTouches[0] : domEvent;
        if (!actual) {
            return;
        }
        const containerPoint = getEventContainerPoint(actual, map._containerDOM),
            coordinate = map.containerPointToCoordinate(containerPoint);
        if (eventType === 'touchstart') {
            preventDefault(domEvent);
        }

        let geometryCursorStyle = null;
        const tops = this.target.getRenderer().getTopElements();
        const topOnlyEvent = (isMousedown || eventType === 'click') && domEvent.button !== 2;
        for (let i = 0; i < tops.length; i++) {
            if (tops[i].hitTest(containerPoint)) {
                const cursor = tops[i].options['cursor'];
                if (cursor) {
                    geometryCursorStyle = cursor;
                }
                if (topOnlyEvent || tops[i].events && tops[i].events.indexOf(eventType) >= 0) {
                    const e = { target: map, type: eventType, domEvent, containerPoint };
                    if (topOnlyEvent) {
                        map._setPriorityCursor(geometryCursorStyle);
                        tops[i].mousedown(e);
                        return;
                    } else {
                        tops[i].onEvent(e);
                    }
                }
            }
        }

        const layers = map._getLayers(layer => {
            if (layer.identify && layer.options['geometryEvents']) {
                return true;
            }
            return false;
        });
        map._setPriorityCursor(geometryCursorStyle);
        if (!layers.length) {
            return;
        }

        const identifyOptions = {
            'includeInternals': true,
            //return only one geometry on top,
            'filter': geometry => {
                if (!(geometry instanceof Geometry)) {
                    return false;
                }
                const eventToFire = geometry._getEventTypeToFire(domEvent);
                if (eventType === 'mousemove') {
                    if (!geometryCursorStyle && geometry.options['cursor']) {
                        geometryCursorStyle = geometry.options['cursor'];
                    }
                    if (!geometry.listens('mousemove') && !geometry.listens('mouseover') && !geometry.listens('mouseenter')) {
                        return false;
                    }
                } else if (!geometry.listens(eventToFire) && !geometry.listens(oneMoreEvent)) {
                    return false;
                }

                return true;
            },
            'count': 1,
            'containerPoint': containerPoint,
            'onlyVisible' : map.options['onlyVisibleGeometryEvents'],
            'layers': layers
        };
        const callback = fireGeometryEvent.bind(this);

        if (eventType === 'mousemove' || eventType === 'touchmove') {
            this._queryIdentifyTimeout = map.getRenderer().callInNextFrame(() => {
                if (map.isInteracting()) {
                    return;
                }
                map.identifyAtPoint(identifyOptions, callback);
            });
        } else {
            map.identifyAtPoint(identifyOptions, callback);
        }

        function fireGeometryEvent(geometries) {
            let propagation = true;
            if (eventType === 'mousemove') {
                const geoMap = {};
                if (geometries.length > 0) {
                    for (let i = geometries.length - 1; i >= 0; i--) {
                        const geo = geometries[i];
                        if (!(geo instanceof Geometry)) {
                            continue;
                        }
                        const iid = geo._getInternalId();
                        geoMap[iid] = geo;
                        geo._onEvent(domEvent);
                        if (!this._prevOverGeos || !this._prevOverGeos.geomap[iid]) {
                            geo._onEvent(domEvent, 'mouseenter');
                        }
                        propagation = geo._onEvent(domEvent, 'mouseover');
                    }
                }

                map._setPriorityCursor(geometryCursorStyle);

                const oldTargets = this._prevOverGeos && this._prevOverGeos.geos;
                this._prevOverGeos = {
                    'geos' : geometries,
                    'geomap' : geoMap
                };
                if (oldTargets && oldTargets.length > 0) {
                    for (let i = oldTargets.length - 1; i >= 0; i--) {
                        const oldTarget = oldTargets[i];
                        if (!(oldTarget instanceof Geometry)) {
                            continue;
                        }
                        const oldTargetId = oldTargets[i]._getInternalId();
                        /**
                         * 鼠标经过的新位置中不包含老的目标geometry
                         */
                        if (!geoMap[oldTargetId]) {
                            propagation = oldTarget._onEvent(domEvent, 'mouseout');
                        }
                    }
                }

            } else {
                if (!geometries || !geometries.length) { return; }
                for (let i = geometries.length - 1; i >= 0; i--) {
                    if (!(geometries[i] instanceof Geometry)) {
                        continue;
                    }
                    propagation = geometries[i]._onEvent(domEvent);
                    if (oneMoreEvent) {
                        geometries[i]._onEvent(domEvent, oneMoreEvent);
                    }
                    break;
                }
            }
            if (propagation === false) {
                stopPropagation(domEvent);
            }
        }

    }
}

Map.mergeOptions({
    'geometryEvents': true,
    'onlyVisibleGeometryEvents' : true
});

Map.addOnLoadHook('addHandler', 'geometryEvents', MapGeometryEventsHandler);

export default MapGeometryEventsHandler;
<commit_msg>happy lint<commit_after>import { now } from '../../core/util';
import { on, off, getEventContainerPoint, preventDefault, stopPropagation } from '../../core/util/dom';
import Handler from '../../handler/Handler';
import Geometry from '../../geometry/Geometry';
import Map from '../Map';

const EVENTS =
    /**
     * mousedown event
     * @event Geometry#mousedown
     * @type {Object}
     * @property {String} type                    - mousedown
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'mousedown ' +
    /**
     * mouseup event
     * @event Geometry#mouseup
     * @type {Object}
     * @property {String} type                    - mouseup
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'mouseup ' +
    /**
     * mousemove event
     * @event Geometry#mousemove
     * @type {Object}
     * @property {String} type                    - mousemove
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'mousemove ' +
    /**
     * click event
     * @event Geometry#click
     * @type {Object}
     * @property {String} type                    - click
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'click ' +
    /**
     * dblclick event
     * @event Geometry#dblclick
     * @type {Object}
     * @property {String} type                    - dblclick
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'dblclick ' +
    /**
     * contextmenu event
     * @event Geometry#contextmenu
     * @type {Object}
     * @property {String} type                    - contextmenu
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'contextmenu ' +
    /**
     * touchstart event
     * @event Geometry#touchstart
     * @type {Object}
     * @property {String} type                    - touchstart
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'touchstart ' +
    /**
     * touchmove event
     * @event Geometry#touchmove
     * @type {Object}
     * @property {String} type                    - touchmove
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'touchmove ' +
    /**
     * touchend event
     * @event Geometry#touchend
     * @type {Object}
     * @property {String} type                    - touchend
     * @property {Geometry} target            - the Geometry fires event
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    'touchend';
    /**
     * mouseenter event for geometry
     * @event Geometry#mouseenter
     * @type {Object}
     * @property {String} type                    - mouseenter
     * @property {Geometry} target       - the geometry fires mouseenter
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    /**
     * mouseover event for geometry
     * @event Geometry#mouseover
     * @type {Object}
     * @property {String} type                    - mouseover
     * @property {Geometry} target       - the geometry fires mouseover
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */
    /**
     * mouseout event for geometry
     * @event Geometry#mouseout
     * @type {Object}
     * @property {String} type                    - mouseout
     * @property {Geometry} target       - the geometry fires mouseout
     * @property {Coordinate} coordinate - coordinate of the event
     * @property {Point} containerPoint  - container point of the event
     * @property {Point} viewPoint       - view point of the event
     * @property {Event} domEvent                 - dom event
     */

class MapGeometryEventsHandler extends Handler {

    addHooks() {
        const map = this.target;
        const dom = map._panels.allLayers || map._containerDOM;
        on(dom, EVENTS, this._identifyGeometryEvents, this);
    }

    removeHooks() {
        const map = this.target;
        const dom = map._panels.allLayers || map._containerDOM;
        off(dom, EVENTS, this._identifyGeometryEvents, this);
    }

    _identifyGeometryEvents(domEvent, type) {
        const map = this.target;
        if (map.isInteracting() || map._ignoreEvent(domEvent)) {
            return;
        }
        let oneMoreEvent = null;
        const eventType = type || domEvent.type;
        // ignore click lasted for more than 300ms.
        const isMousedown = eventType === 'mousedown' || (eventType === 'touchstart' && domEvent.touches && domEvent.touches.length === 1);
        if (isMousedown) {
            this._mouseDownTime = now();
        } else if ((eventType === 'click' || eventType === 'touchend') && this._mouseDownTime) {
            const downTime = this._mouseDownTime;
            delete this._mouseDownTime;
            const time = now();
            if (time - downTime > 300) {
                if (eventType === 'click') {
                    return;
                }
            } else if (eventType === 'touchend') {
                oneMoreEvent = 'click';
            }
        }

        const actual = domEvent.touches && domEvent.touches.length > 0 ?
            domEvent.touches[0] : domEvent.changedTouches && domEvent.changedTouches.length > 0 ?
                domEvent.changedTouches[0] : domEvent;
        if (!actual) {
            return;
        }
        const containerPoint = getEventContainerPoint(actual, map._containerDOM);
        if (eventType === 'touchstart') {
            preventDefault(domEvent);
        }

        let geometryCursorStyle = null;
        const tops = this.target.getRenderer().getTopElements();
        const topOnlyEvent = (isMousedown || eventType === 'click') && domEvent.button !== 2;
        for (let i = 0; i < tops.length; i++) {
            if (tops[i].hitTest(containerPoint)) {
                const cursor = tops[i].options['cursor'];
                if (cursor) {
                    geometryCursorStyle = cursor;
                }
                if (topOnlyEvent || tops[i].events && tops[i].events.indexOf(eventType) >= 0) {
                    const e = { target: map, type: eventType, domEvent, containerPoint };
                    if (topOnlyEvent) {
                        map._setPriorityCursor(geometryCursorStyle);
                        tops[i].mousedown(e);
                        return;
                    } else {
                        tops[i].onEvent(e);
                    }
                }
            }
        }

        const layers = map._getLayers(layer => {
            if (layer.identify && layer.options['geometryEvents']) {
                return true;
            }
            return false;
        });
        map._setPriorityCursor(geometryCursorStyle);
        if (!layers.length) {
            return;
        }

        const identifyOptions = {
            'includeInternals': true,
            //return only one geometry on top,
            'filter': geometry => {
                if (!(geometry instanceof Geometry)) {
                    return false;
                }
                const eventToFire = geometry._getEventTypeToFire(domEvent);
                if (eventType === 'mousemove') {
                    if (!geometryCursorStyle && geometry.options['cursor']) {
                        geometryCursorStyle = geometry.options['cursor'];
                    }
                    if (!geometry.listens('mousemove') && !geometry.listens('mouseover') && !geometry.listens('mouseenter')) {
                        return false;
                    }
                } else if (!geometry.listens(eventToFire) && !geometry.listens(oneMoreEvent)) {
                    return false;
                }

                return true;
            },
            'count': 1,
            'containerPoint': containerPoint,
            'onlyVisible' : map.options['onlyVisibleGeometryEvents'],
            'layers': layers
        };
        const callback = fireGeometryEvent.bind(this);

        if (eventType === 'mousemove' || eventType === 'touchmove') {
            this._queryIdentifyTimeout = map.getRenderer().callInNextFrame(() => {
                if (map.isInteracting()) {
                    return;
                }
                map.identifyAtPoint(identifyOptions, callback);
            });
        } else {
            map.identifyAtPoint(identifyOptions, callback);
        }

        function fireGeometryEvent(geometries) {
            let propagation = true;
            if (eventType === 'mousemove') {
                const geoMap = {};
                if (geometries.length > 0) {
                    for (let i = geometries.length - 1; i >= 0; i--) {
                        const geo = geometries[i];
                        if (!(geo instanceof Geometry)) {
                            continue;
                        }
                        const iid = geo._getInternalId();
                        geoMap[iid] = geo;
                        geo._onEvent(domEvent);
                        if (!this._prevOverGeos || !this._prevOverGeos.geomap[iid]) {
                            geo._onEvent(domEvent, 'mouseenter');
                        }
                        propagation = geo._onEvent(domEvent, 'mouseover');
                    }
                }

                map._setPriorityCursor(geometryCursorStyle);

                const oldTargets = this._prevOverGeos && this._prevOverGeos.geos;
                this._prevOverGeos = {
                    'geos' : geometries,
                    'geomap' : geoMap
                };
                if (oldTargets && oldTargets.length > 0) {
                    for (let i = oldTargets.length - 1; i >= 0; i--) {
                        const oldTarget = oldTargets[i];
                        if (!(oldTarget instanceof Geometry)) {
                            continue;
                        }
                        const oldTargetId = oldTargets[i]._getInternalId();
                        /**
                         * 鼠标经过的新位置中不包含老的目标geometry
                         */
                        if (!geoMap[oldTargetId]) {
                            propagation = oldTarget._onEvent(domEvent, 'mouseout');
                        }
                    }
                }

            } else {
                if (!geometries || !geometries.length) { return; }
                for (let i = geometries.length - 1; i >= 0; i--) {
                    if (!(geometries[i] instanceof Geometry)) {
                        continue;
                    }
                    propagation = geometries[i]._onEvent(domEvent);
                    if (oneMoreEvent) {
                        geometries[i]._onEvent(domEvent, oneMoreEvent);
                    }
                    break;
                }
            }
            if (propagation === false) {
                stopPropagation(domEvent);
            }
        }

    }
}

Map.mergeOptions({
    'geometryEvents': true,
    'onlyVisibleGeometryEvents' : true
});

Map.addOnLoadHook('addHandler', 'geometryEvents', MapGeometryEventsHandler);

export default MapGeometryEventsHandler;
<|endoftext|>


<commit_before>define('Mobile/SalesLogix/Views/OpportunityContact/List', [
    'dojo/_base/declare',
    'dojo/string',
    'Sage/Platform/Mobile/List'
], function(
    declare,
    string,
    List
) {

    return declare('Mobile.SalesLogix.Views.OpportunityContact.List', [List], {
        //Template
        itemTemplate: new Simplate([
            '
{%: $.Contact.NameLF %}
',
            '
',
                '{% if ($.SalesRole) { %}',
                    '{%: $.SalesRole %} | ',
                '{% } %}',
            '{%: $.Contact.Title %}
'
        ]),

        //Localization
        titleText: 'Opportunity Contacts',
        selectTitleText: 'Select Contact',
        activitiesText: 'Activities',
        notesText: 'Notes',
        scheduleText: 'Schedule',

        //View Properties
        id: 'opportunitycontact_list',
        detailView: 'opportunitycontact_detail',
        selectView: 'contact_related',
        insertView: 'opportunitycontact_edit',
        icon: 'content/images/icons/Contacts_24x24.png',
        security: 'Entities/Contact/View',
        queryOrderBy: 'Contact.NameLF',
        expose: false,
        querySelect: [
            'Contact/Account/AccountName',
            'Contact/AccountName',
            'SalesRole',
            'IsPrimary',
            'Contact/NameLF',
            'Contact/Title'
        ],
        resourceKind: 'opportunityContacts',

        complete: function() {
            var view = App.getPrimaryActiveView(),
                selectionModel = view && view.get('selectionModel'),
                entry;
            if (!selectionModel) return;

            if (selectionModel.getSelectionCount() == 0 && view.options.allowEmptySelection)
                ReUI.back();

            var context = App.isNavigationFromResourceKind(['opportunities']),
                selections = selectionModel.getSelections();
            for (var selectionKey in selections)
            {
                entry = {
                    'Opportunity': {'$key': context.key},
                    'Contact': view.entries[selectionKey]
                };
            }

            if (entry)
                this.navigateToInsertView(entry);
        },
        createNavigationOptions: function() {
            var options = {
                query: this.expandExpression(this.options.prefilter),
                selectionOnly: true,
                singleSelect: true,
                singleSelectAction: 'complete',
                allowEmptySelection: false,
                title: this.selectTitleText,
                select: [
                    'Account/AccountName',
                    'AccountName',
                    'NameLF',
                    'Title'
                ],
                tools: {
                    tbar: [{
                        id: 'complete',
                        fn: this.complete,
                        cls: 'invisible',
                        scope: this
                    },{
                        id: 'cancel',
                        side: 'left',
                        fn: ReUI.back,
                        scope: ReUI
                    }]
                }
            };
            return options;
        },
        navigateToInsertView: function(entry) {
            var view = App.getView(this.insertView),
                options = {
                    entry: entry,
                    insert: true
                };
            if (view && options)
                view.show(options, { returnTo: -1 });
        },
        navigateToSelectView: function() {
            var view = App.getView(this.selectView),
                options = this.createNavigationOptions();
            if (view && options)
                view.show(options);
        },
        createToolLayout: function() {
            return this.tools || (this.tools = {
                'tbar': [{
                    id: 'associate',
                    icon: 'content/images/icons/add_24.png',
                    action: 'navigateToSelectView',
                    security: App.getViewSecurity(this.insertView, 'insert')
                }]
            });
        },
        formatSearchQuery: function(searchQuery) {
            return string.substitute('(upper(Contact.NameLF) like "${0}%")', [this.escapeSearchQuery(searchQuery.toUpperCase())]);
        }
    });
});<commit_msg>[Defect #12089400] Overlap occurred on 'Select Contact' view page in opportunity entity<commit_after>define('Mobile/SalesLogix/Views/OpportunityContact/List', [
    'dojo/_base/declare',
    'dojo/string',
    'Sage/Platform/Mobile/List'
], function(
    declare,
    string,
    List
) {

    return declare('Mobile.SalesLogix.Views.OpportunityContact.List', [List], {
        //Template
        itemTemplate: new Simplate([
            '
{%: $.Contact.NameLF %}
',
            '
',
                '{% if ($.SalesRole) { %}',
                    '{%: $.SalesRole %} | ',
                '{% } %}',
            '{%: $.Contact.Title %}
'
        ]),

        //Localization
        titleText: 'Opportunity Contacts',
        selectTitleText: 'Select Contact',
        activitiesText: 'Activities',
        notesText: 'Notes',
        scheduleText: 'Schedule',

        //View Properties
        id: 'opportunitycontact_list',
        detailView: 'opportunitycontact_detail',
        selectView: 'contact_related',
        insertView: 'opportunitycontact_edit',
        icon: 'content/images/icons/Contacts_24x24.png',
        security: 'Entities/Contact/View',
        queryOrderBy: 'Contact.NameLF',
        expose: false,
        querySelect: [
            'Contact/Account/AccountName',
            'Contact/AccountName',
            'SalesRole',
            'IsPrimary',
            'Contact/NameLF',
            'Contact/Title'
        ],
        resourceKind: 'opportunityContacts',

        complete: function() {
            var view = App.getPrimaryActiveView(),
                selectionModel = view && view.get('selectionModel'),
                entry;
            if (!selectionModel) return;

            if (selectionModel.getSelectionCount() == 0 && view.options.allowEmptySelection)
                ReUI.back();

            var context = App.isNavigationFromResourceKind(['opportunities']),
                selections = selectionModel.getSelections();
            for (var selectionKey in selections)
            {
                entry = {
                    'Opportunity': {'$key': context.key},
                    'Contact': view.entries[selectionKey]
                };
            }

            if (entry)
                this.navigateToInsertView(entry);
        },
        createNavigationOptions: function() {
            var options = {
                query: this.expandExpression(this.options.prefilter),
                selectionOnly: true,
                singleSelect: true,
                singleSelectAction: 'complete',
                allowEmptySelection: false,
                enableActions: false,
                title: this.selectTitleText,
                select: [
                    'Account/AccountName',
                    'AccountName',
                    'NameLF',
                    'Title'
                ],
                tools: {
                    tbar: [{
                        id: 'complete',
                        fn: this.complete,
                        cls: 'invisible',
                        scope: this
                    },{
                        id: 'cancel',
                        side: 'left',
                        fn: ReUI.back,
                        scope: ReUI
                    }]
                }
            };
            return options;
        },
        navigateToInsertView: function(entry) {
            var view = App.getView(this.insertView),
                options = {
                    entry: entry,
                    insert: true
                };
            if (view && options)
                view.show(options, { returnTo: -1 });
        },
        navigateToSelectView: function() {
            var view = App.getView(this.selectView),
                options = this.createNavigationOptions();
            if (view && options)
                view.show(options);
        },
        createToolLayout: function() {
            return this.tools || (this.tools = {
                'tbar': [{
                    id: 'associate',
                    icon: 'content/images/icons/add_24.png',
                    action: 'navigateToSelectView',
                    security: App.getViewSecurity(this.insertView, 'insert')
                }]
            });
        },
        formatSearchQuery: function(searchQuery) {
            return string.substitute('(upper(Contact.NameLF) like "${0}%")', [this.escapeSearchQuery(searchQuery.toUpperCase())]);
        }
    });
});<|endoftext|>


<commit_before>/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "_" }] */
/* globals isNumeric_, wrapValue_ */

/**
 * This callback type is called `queryCallback`.
 *  Callback should utilize the Query parameter to send a request and return the response.
 *
 * @callback queryCallback
 * @param {object} query the Structured Query to utilize in the query request {@link FirestoreQuery_}
 * @returns [object] response of the sent query
 */

/**
 * An object that acts as a Query to be a structured query.
 * Chain methods to update query. Must call .execute to send request.
 *
 * @constructor
 * @private
 * @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery Firestore Structured Query}
 * @param {string} from the base collection to query
 * @param {queryCallback} callback the function that is executed with the internally compiled query
 */
var FirestoreQuery_ = function (from, callback) {
  const this_ = this

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Operator_1 FieldFilter Operator}
  const fieldOps = {
    '==': 'EQUAL',
    '===': 'EQUAL',
    '<': 'LESS_THAN',
    '<=': 'LESS_THAN_OR_EQUAL',
    '>': 'GREATER_THAN',
    '>=': 'GREATER_THAN_OR_EQUAL',
    'array-contains': 'ARRAY_CONTAINS'
  }

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Operator_2 FieldFilter Operator}
  const unaryOps = {
    'nan': 'IS_NAN',
    'null': 'IS_NULL'
  }

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#FieldReference Field Reference}
  const fieldRef = function (field) {
    return {fieldPath: field}
  }
  const filter = function (field, operator, value) {
    // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#FieldFilter Field Filter}
    if (operator in fieldOps) {
      if (value == null) { // Covers null and undefined values
        operator = 'null'
      } else if (isNumberNaN(value)) { // Covers NaN
        operator = 'nan'
      } else {
        return {
          fieldFilter: {
            field: fieldRef(field),
            op: fieldOps[operator],
            value: wrapValue_(value)
          }
        }
      }
    }

    // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#UnaryFilter Unary Filter}
    if (operator.toLowerCase() in unaryOps) {
      return {
        unaryFilter: {
          field: fieldRef(field),
          op: unaryOps[operator]
        }
      }
    }
    throw new Error('Invalid Operator given ' + operator)
  }

  const query = {}
  if (from) {
    query.from = [{collectionId: from}]
  }

  /**
   * Select Query which can narrow which fields to return.
   *  Can be repeated if multiple fields are needed in the response.
   *
   * @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Projection Select}
   * @param {string} field The field to narrow down (if empty, returns name of document)
   * @returns {object} this query object for chaining
   */
  this.select = function (field) {
    if (!query.select) {
      query.select = {fields: []}
    }
    if (!field || !field.trim()) { // Catch undefined or blank strings
      field = '__name__'
    }

    query.select.fields.push(fieldRef(field))
    return this_
  }

  /**
   * Filter Query by a given field and operator (or additionally a value).
   *  Can be repeated if multiple filters required.
   *  Results must satisfy all filters.
   *
   * @param {string} field The field to reference for filtering
   * @param {string} operator The operator to filter by. {@link fieldOps} {@link unaryOps}
   * @param {*} [value] Object to set the field value to. Null if using a unary operator.
   * @returns {object} this query object for chaining
   */
  this.where = function (field, operator, value) {
    if (query.where) {
      if (!query.where.compositeFilter) {
        query.where = {
          compositeFilter: {
            op: 'AND', // Currently "OR" is unsupported
            filters: [
              query.where
            ]
          }
        }
      }
      query.where.compositeFilter.filters.push(filter(field, operator, value))
    } else {
      query.where = filter(field, operator, value)
    }
    return this_
  }

  /**
   * Orders the Query results based on a field and specific direction.
   *  Can be repeated if additional ordering is needed.
   *
   * @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Projection Select}
   * @param {string} field The field to order by.
   * @param {string} dir The direction to order the field by. Should be one of "asc" or "desc". Defaults to Ascending.
   * @returns {object} this query object for chaining
   */
  this.orderBy = function (field, dir) {
    if (!query.orderBy) {
      query.orderBy = []
    }
    const isDesc = dir && (dir.substr(0, 3).toUpperCase() === 'DEC' || dir.substr(0, 4).toUpperCase() === 'DESC')

    query.orderBy.push({
      field: fieldRef(field),
      direction: isDesc ? 'DESCENDING' : 'ASCENDING'
    })
    return this_
  }

  /**
   * Offsets the Query results by a given number of documents.
   *
   * @param {number} offset Number of results to skip
   * @returns {object} this query object for chaining
   */
  this.offset = function (offset) {
    if (!isNumeric_(offset)) {
      throw new TypeError('Offset is not a valid number!')
    }
    query.offset = offset
    return this_
  }

  /**
   * Limits the amount Query results returned.
   *
   * @param {number} limit Number of results limit
   * @returns {object} this query object for chaining
   */
  this.limit = function (limit) {
    if (!isNumeric_(limit)) {
      throw new TypeError('Limit is not a valid number!')
    }
    query.limit = limit
    return this_
  }

  /**
   * Executes the query with the given callback method and the generated query.
   *  Must be used at the end of any query for execution.
   *
   * @returns {object} The query results from the execution
   */
  this.execute = function () {
    return callback(query) // Not using callback.bind due to debugging limitations of GAS
  }
}
<commit_msg>rename array_contains to contains<commit_after>/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "_" }] */
/* globals isNumeric_, wrapValue_ */

/**
 * This callback type is called `queryCallback`.
 *  Callback should utilize the Query parameter to send a request and return the response.
 *
 * @callback queryCallback
 * @param {object} query the Structured Query to utilize in the query request {@link FirestoreQuery_}
 * @returns [object] response of the sent query
 */

/**
 * An object that acts as a Query to be a structured query.
 * Chain methods to update query. Must call .execute to send request.
 *
 * @constructor
 * @private
 * @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery Firestore Structured Query}
 * @param {string} from the base collection to query
 * @param {queryCallback} callback the function that is executed with the internally compiled query
 */
var FirestoreQuery_ = function (from, callback) {
  const this_ = this

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Operator_1 FieldFilter Operator}
  const fieldOps = {
    '==': 'EQUAL',
    '===': 'EQUAL',
    '<': 'LESS_THAN',
    '<=': 'LESS_THAN_OR_EQUAL',
    '>': 'GREATER_THAN',
    '>=': 'GREATER_THAN_OR_EQUAL',
    'contains': 'ARRAY_CONTAINS'
  }

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Operator_2 FieldFilter Operator}
  const unaryOps = {
    'nan': 'IS_NAN',
    'null': 'IS_NULL'
  }

  // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#FieldReference Field Reference}
  const fieldRef = function (field) {
    return {fieldPath: field}
  }
  const filter = function (field, operator, value) {
    // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#FieldFilter Field Filter}
    if (operator in fieldOps) {
      if (value == null) { // Covers null and undefined values
        operator = 'null'
      } else if (isNumberNaN(value)) { // Covers NaN
        operator = 'nan'
      } else {
        return {
          fieldFilter: {
            field: fieldRef(field),
            op: fieldOps[operator],
            value: wrapValue_(value)
          }
        }
      }
    }

    // @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#UnaryFilter Unary Filter}
    if (operator.toLowerCase() in unaryOps) {
      return {
        unaryFilter: {
          field: fieldRef(field),
          op: unaryOps[operator]
        }
      }
    }
    throw new Error('Invalid Operator given ' + operator)
  }

  const query = {}
  if (from) {
    query.from = [{collectionId: from}]
  }

  /**
   * Select Query which can narrow which fields to return.
   *  Can be repeated if multiple fields are needed in the response.
   *
   * @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Projection Select}
   * @param {string} field The field to narrow down (if empty, returns name of document)
   * @returns {object} this query object for chaining
   */
  this.select = function (field) {
    if (!query.select) {
      query.select = {fields: []}
    }
    if (!field || !field.trim()) { // Catch undefined or blank strings
      field = '__name__'
    }

    query.select.fields.push(fieldRef(field))
    return this_
  }

  /**
   * Filter Query by a given field and operator (or additionally a value).
   *  Can be repeated if multiple filters required.
   *  Results must satisfy all filters.
   *
   * @param {string} field The field to reference for filtering
   * @param {string} operator The operator to filter by. {@link fieldOps} {@link unaryOps}
   * @param {*} [value] Object to set the field value to. Null if using a unary operator.
   * @returns {object} this query object for chaining
   */
  this.where = function (field, operator, value) {
    if (query.where) {
      if (!query.where.compositeFilter) {
        query.where = {
          compositeFilter: {
            op: 'AND', // Currently "OR" is unsupported
            filters: [
              query.where
            ]
          }
        }
      }
      query.where.compositeFilter.filters.push(filter(field, operator, value))
    } else {
      query.where = filter(field, operator, value)
    }
    return this_
  }

  /**
   * Orders the Query results based on a field and specific direction.
   *  Can be repeated if additional ordering is needed.
   *
   * @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1beta1/StructuredQuery#Projection Select}
   * @param {string} field The field to order by.
   * @param {string} dir The direction to order the field by. Should be one of "asc" or "desc". Defaults to Ascending.
   * @returns {object} this query object for chaining
   */
  this.orderBy = function (field, dir) {
    if (!query.orderBy) {
      query.orderBy = []
    }
    const isDesc = dir && (dir.substr(0, 3).toUpperCase() === 'DEC' || dir.substr(0, 4).toUpperCase() === 'DESC')

    query.orderBy.push({
      field: fieldRef(field),
      direction: isDesc ? 'DESCENDING' : 'ASCENDING'
    })
    return this_
  }

  /**
   * Offsets the Query results by a given number of documents.
   *
   * @param {number} offset Number of results to skip
   * @returns {object} this query object for chaining
   */
  this.offset = function (offset) {
    if (!isNumeric_(offset)) {
      throw new TypeError('Offset is not a valid number!')
    }
    query.offset = offset
    return this_
  }

  /**
   * Limits the amount Query results returned.
   *
   * @param {number} limit Number of results limit
   * @returns {object} this query object for chaining
   */
  this.limit = function (limit) {
    if (!isNumeric_(limit)) {
      throw new TypeError('Limit is not a valid number!')
    }
    query.limit = limit
    return this_
  }

  /**
   * Executes the query with the given callback method and the generated query.
   *  Must be used at the end of any query for execution.
   *
   * @returns {object} The query results from the execution
   */
  this.execute = function () {
    return callback(query) // Not using callback.bind due to debugging limitations of GAS
  }
}
<|endoftext|>


<commit_before>
/**
 * A action set event dispatcher is in charge to triggers actions binded on an event.
 *
 * @constructor FORGE.ActionEventDispatcher
 * @param {FORGE.Viewer} viewer - {@link FORGE.Viewer} reference.
 * @param {string} name - The name of the event.
 * @extends {FORGE.BaseObject}
 */
FORGE.ActionEventDispatcher = function(viewer, name)
{
    /**
     * The viewer reference.
     * @name FORGE.ActionEventDispatcher#_viewer
     * @type {FORGE.Viewer}
     * @private
     */
    this._viewer = viewer;

    /**
     * Event name
     * @name FORGE.ActionEventDispatcher#_name
     * @type {string}
     * @private
     */
    this._name = name;

    /**
     * The actions uid list of the set.
     * @name  FORGE.ActionEventDispatcher#_actions
     * @type {Array<string>}
     * @private
     */
    this._actions = [];

    FORGE.BaseObject.call(this, "ActionEventDispatcher");
};

FORGE.ActionEventDispatcher.prototype = Object.create(FORGE.BaseObject.prototype);
FORGE.ActionEventDispatcher.prototype.constructor = FORGE.ActionEventDispatcher;

/**
 * Boot sequence.
 * @method FORGE.ActionEventDispatcher#addActions
 * @param {(string|Array<string>)} actions - The actions uids you want to add
 */
FORGE.ActionEventDispatcher.prototype.addActions = function(actions)
{
    if(FORGE.Utils.isArrayOf(actions, "string") === true || FORGE.Utils.isTypeOf(actions, "string"))
    {
        this._actions = this._actions.concat(actions);
    }
};

/**
 * This method execute every actions binded on this event dispatcher.
 * @method FORGE.ActionEventDispatcher#dispatch
 */
FORGE.ActionEventDispatcher.prototype.dispatch = function()
{
    for(var i = 0, ii = this._actions.length; i < ii; i++)
    {
        var action = this._viewer.actions.get(this._actions[i]);

        if(typeof action !== "undefined" && action !== null)
        {
            action.execute();
        }
    }
};

/**
 * Destroy sequence.
 * @method  FORGE.ActionEventDispatcher#destroy
 */
FORGE.ActionEventDispatcher.prototype.destroy = function()
{
    this._viewer = null;

    FORGE.BaseObject.prototype.destroy.call(this);
};
<commit_msg>Actiondispatcher can now reschedule the destroy if destroy is called during event dispatch<commit_after>
/**
 * A action set event dispatcher is in charge to triggers actions binded on an event.
 *
 * @constructor FORGE.ActionEventDispatcher
 * @param {FORGE.Viewer} viewer - {@link FORGE.Viewer} reference.
 * @param {string} name - The name of the event.
 * @extends {FORGE.BaseObject}
 */
FORGE.ActionEventDispatcher = function(viewer, name)
{
    /**
     * The viewer reference.
     * @name FORGE.ActionEventDispatcher#_viewer
     * @type {FORGE.Viewer}
     * @private
     */
    this._viewer = viewer;

    /**
     * Event name
     * @name FORGE.ActionEventDispatcher#_name
     * @type {string}
     * @private
     */
    this._name = name;

    /**
     * The actions uid list of the set.
     * @name  FORGE.ActionEventDispatcher#_actions
     * @type {Array<string>}
     * @private
     */
    this._actions = [];

    /**
     * Flag to know if the dispatcher is currently dispatching the events.
     * It happen that the dispatcher destroy method is called during the dispatching the events.
     * This allows us to re schedule the destroy call after all events have been executed.
     * @name  FORGE.ActionEventDispatcher#_dispatching
     * @type {boolean}
     * @private
     */
    this._dispatching = false;

    /**
     * Is the destroy method have been called during the dispatching?
     * @name  FORGE.ActionEventDispatcher#_pendingDestroy
     * @type {boolean}
     * @private
     */
    this._pendingDestroy = false;

    FORGE.BaseObject.call(this, "ActionEventDispatcher");
};

FORGE.ActionEventDispatcher.prototype = Object.create(FORGE.BaseObject.prototype);
FORGE.ActionEventDispatcher.prototype.constructor = FORGE.ActionEventDispatcher;

/**
 * Boot sequence.
 * @method FORGE.ActionEventDispatcher#addActions
 * @param {(string|Array<string>)} actions - The actions uids you want to add
 */
FORGE.ActionEventDispatcher.prototype.addActions = function(actions)
{
    if(FORGE.Utils.isArrayOf(actions, "string") === true || FORGE.Utils.isTypeOf(actions, "string"))
    {
        this._actions = this._actions.concat(actions);
    }
};

/**
 * This method execute every actions binded on this event dispatcher.
 * @method FORGE.ActionEventDispatcher#dispatch
 */
FORGE.ActionEventDispatcher.prototype.dispatch = function()
{
    this._dispatching = true;

    for(var i = 0, ii = this._actions.length; i < ii; i++)
    {
        var action = this._viewer.actions.get(this._actions[i]);

        if(typeof action !== "undefined" && action !== null)
        {
            action.execute();
        }
    }

    this._dispatching = false;

    if(this._pendingDestroy === true)
    {
        this.destroy();
    }
};

/**
 * Destroy sequence.
 * @method  FORGE.ActionEventDispatcher#destroy
 */
FORGE.ActionEventDispatcher.prototype.destroy = function()
{
    //If dispatching, wait for the dispatch is complete to execute the destroy
    if(this._dispatching === true)
    {
        this._pendingDestroy = true;
        return;
    }

    this._viewer = null;

    FORGE.BaseObject.prototype.destroy.call(this);
};
<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';
import {execSync} from 'child_process';
import Post from '../../controller/api/post';
import Base from './base';

const PATH = path.join(think.RUNTIME_PATH, 'importMarkdownFileToFirekylin');
export default class extends Base {
  constructor(think) {
    super(think);
    this._think = think;
  }
  /**
   * 导入用户
   */
  async user() {
    return 0;
  }

  /**
   * 导入分类
   */
  async category() {
    return 0;
  }

  /**
   * 导入标签
   */
  async tag() {
    return 0;
  }

  /**
   * 导入文章
   */
  async post(posts = []) {
    if(!Array.isArray(posts)) {
      return 0;
    }

    const postsPromise = posts.map(async item => {
      try{
        //获取用户
        const user = await this._think.session('userInfo');

        const post = {
          title: item.title,
          pathname: item.pathname,
          markdown_content: item.markdown_content,
          create_time: this.formatDate(new Date(item.created_at)),
          update_time: this.formatDate(new Date(item.updated_at)),
          status: 3,
          user_id: user.id,
          comment_num: 0,
          allow_comment: 1,
          is_public: 1,
          type: 0
        };
        await Post.prototype.getContentAndSummary(post);
        await this.postModelInstance.addPost(post);
      } catch(e) {
        console.log(e);  // eslint-disable-line no-console
      }
    });
    Promise.all(postsPromise);

    return posts.length;
  }

  /**
   * 导入页面
   */
  async page() {
    return 0;
  }

  parseFile(file) {
    try {
      execSync(`rm -rf ${PATH}; mkdir ${PATH}; cd ${PATH}; tar zxvf ${file.path}`);
      let files = fs.readdirSync(PATH, {encoding: 'utf-8'});
      if(!files.length) { return []; }

      return files.map(function(file) {
        let tar = path.join(PATH, file);
        let title = file.split('.').slice(0, -1).join('');
        let content = fs.readFileSync(tar, {encoding: 'utf-8'});
        let stat = fs.statSync(tar);
        return {
          created_at: stat.birthtime.getTime(),
          updated_at: stat.mtime.getTime(),
          title,
          pathname: title,
          markdown_content: content
        };
      });
    } catch(e) {
      throw new Error(e);
    }
  }

  /**
   * 执行导入
   */
  async run(file) {
    return await this.importData(this.parseFile(file));
  }
}
<commit_msg>fix #338<commit_after>import fs from 'fs';
import path from 'path';
import {execSync} from 'child_process';
import Post from '../../controller/api/post';
import Base from './base';

const PATH = path.join(think.RUNTIME_PATH, 'importMarkdownFileToFirekylin');
export default class extends Base {
  constructor(think) {
    super(think);
    this._think = think;
  }
  /**
   * 导入用户
   */
  async user() {
    return 0;
  }

  /**
   * 导入分类
   */
  async category() {
    return 0;
  }

  /**
   * 导入标签
   */
  async tag() {
    return 0;
  }

  /**
   * 导入文章
   */
  async post(posts = []) {
    if(!Array.isArray(posts)) {
      return 0;
    }

    const postsPromise = posts.map(async item => {
      try{
        //获取用户
        const user = await this._think.session('userInfo');

        let post = {
          title: item.title,
          pathname: item.pathname,
          markdown_content: item.markdown_content,
          create_time: this.formatDate(new Date(item.created_at)),
          update_time: this.formatDate(new Date(item.updated_at)),
          status: 3,
          user_id: user.id,
          comment_num: 0,
          allow_comment: 1,
          is_public: 1,
          type: 0
        };
        post = await think.model('post', null, 'admin').getContentAndSummary(post);
        await this.postModelInstance.addPost(post);
      } catch(e) {
        console.log(e);  // eslint-disable-line no-console
      }
    });
    Promise.all(postsPromise);

    return posts.length;
  }

  /**
   * 导入页面
   */
  async page() {
    return 0;
  }

  parseFile(file) {
    try {
      execSync(`rm -rf ${PATH}; mkdir ${PATH}; cd ${PATH}; tar zxvf ${file.path}`);
      let files = fs.readdirSync(PATH, {encoding: 'utf-8'});
      if(!files.length) { return []; }

      return files.map(function(file) {
        let tar = path.join(PATH, file);
        let title = file.split('.').slice(0, -1).join('');
        let content = fs.readFileSync(tar, {encoding: 'utf-8'});
        let stat = fs.statSync(tar);
        return {
          created_at: stat.birthtime.getTime(),
          updated_at: stat.mtime.getTime(),
          title,
          pathname: title,
          markdown_content: content
        };
      });
    } catch(e) {
      throw new Error(e);
    }
  }

  /**
   * 执行导入
   */
  async run(file) {
    return await this.importData(this.parseFile(file));
  }
}
<|endoftext|>


<commit_before>import CommandModule from "../CommandModule";
import ChatApi from "../../util/ChatApi";
import { dependencies as Inject } from "needlepoint";
import Configuration from "../../util/Configuration";
import Promise from "bluebird";
import googl from "goo.gl";

@Inject(Configuration, ChatApi)
export default class WhoDisCommand extends CommandModule {
    constructor(config, api) {
        super();

        this.config = config;
        this.api = api;

        this.recentMessagesCount = config.getModuleConfig(this, "recentMessagesCount");

        googl.setKey(config.getModuleConfig(this, "googlApiKey"));
    }

    getCommand() {
        return "whodis";
    }

    getDescription() {
        return "Runs a reverse image search on the last picture";
    }

    getUsage() {
        return "";
    }

    validate(msg, argsString) {
        return true;
    }

    execute(msg, argsString) {
        let endTypingIndicator = () => {};

        return this.api
            .markAsRead(msg.threadID)
            .then(() => this.api.sendTypingIndicator(msg.threadID))
            .then(endFn => endTypingIndicator = endFn)
            .catch(err => {
                console.warn(err);
                return Promise.resolve();
            })
            .then(() => this.api.getThreadHistory(msg.threadID, 0, this.recentMessagesCount))
            .then(history => {
                const photos = history
                    .filter(m => m.attachments.length > 0)
                    .filter(m => m.attachments.every(a => a.type == "photo"))
                    .sort((m1, m2) => m2.timestamp - m1.timestamp);

                if (photos.length == 0) {
                    this.api.sendMessage("??? \uD83D\uDC68\uD83C\uDFFF ???\n\n" +
                        "No photos found within the last " + this.recentMessagesCount + " messages.",
                        msg.threadID
                    );

                    return Promise.reject("No photos found");
                }

                const lastPhoto = photos[0].attachments[0];
                const url = lastPhoto.hiresUrl || lastPhoto.previewUrl || lastPhoto.thumbnailUrl;

                if (!url) return Promise.reject(new Error("Could not get photo's URL"));

                return url;
            })
            .catch(err => {
                this.api.sendMessage("Sorry, something went wrong. \u{1F615}", msg.threadID);
                throw err;
            })
            .then(url => this.getResultWithShortUrls(url))
            .then(resultText => this.api.sendMessage(resultText, msg.threadID))
            .finally(() => {
                endTypingIndicator();
            });
    }

    getResultWithShortUrls(url) {
        return Promise.all([
            googl.shorten(this.getGoogleUrl(url)),
            googl.shorten(this.getBingUrl(url)),
            googl.shorten(this.getTinEyeUrl(url))
        ]).then(result => {
            const googleShortUrl = result[0];
            const bingShortUrl = result[1];
            const tinEyeShortUrl = result[2];

            let text = "\u{1F50D} Reverse image search:\n\n";
            text += "\u{1F516} Google: " + googleShortUrl + " \n";
            text += "\u{1F516} Bing: " + bingShortUrl + " \n";
            text += "\u{1F516} TinEye: " + tinEyeShortUrl;

            return text;
        });
    }

    getGoogleUrl(url) {
        return "https://images.google.com/searchbyimage?image_url=" + encodeURIComponent(url);
    }

    getBingUrl(url) {
        return "https://www.bing.com/images/search?q=imgurl:"
            + encodeURIComponent(url)
            + "&view=detailv2&selectedIndex=0&pageurl=&mode=ImageViewer&iss=sbi";
    }

    getTinEyeUrl(url) {
        return "https://tineye.com/search?url=" + encodeURIComponent(url);
    }
}<commit_msg>Refactor WhoDisCommand<commit_after>import CommandModule from "../CommandModule";
import ChatApi from "../../util/ChatApi";
import { dependencies as Inject } from "needlepoint";
import Configuration from "../../util/Configuration";
import Promise from "bluebird";
import googl from "goo.gl";

@Inject(Configuration, ChatApi)
export default class WhoDisCommand extends CommandModule {
    constructor(config, api) {
        super();

        this.config = config;
        this.api = api;

        this.recentMessagesCount = config.getModuleConfig(this, "recentMessagesCount");

        googl.setKey(config.getModuleConfig(this, "googlApiKey"));
    }

    getCommand() {
        return "whodis";
    }

    getDescription() {
        return "Runs a reverse image search on the last picture";
    }

    getUsage() {
        return "";
    }

    validate(msg, argsString) {
        return true;
    }

    execute(msg, argsString) {
        let endTypingIndicator = () => {};

        return this.api
            .markAsRead(msg.threadID)
            .then(() => this.api.sendTypingIndicator(msg.threadID))
            .then(endFn => endTypingIndicator = endFn)
            .catch(err => {
                console.warn(err);
                return Promise.resolve();
            })
            .then(() => this.api.getThreadHistory(msg.threadID, 0, this.recentMessagesCount))
            .then(history => this.getLastPhotoUrl(msg, history))
            .catch(err => {
                this.api.sendMessage("Sorry, something went wrong. \u{1F615}", msg.threadID);
                throw err;
            })
            .then(url => WhoDisCommand.getResultWithShortUrls(url))
            .then(resultText => this.api.sendMessage(resultText, msg.threadID))
            .finally(() => {
                endTypingIndicator();
            });
    }

    /**
     * @param {*} msgCtx
     * @param {Array.<*>} history
     * @return {string}
     * @private
     */
    getLastPhotoUrl(msgCtx, history) {
        const photos = history
            .filter(m => m.attachments.length > 0)
            .filter(m => m.attachments.every(a => a.type == "photo"))
            .sort((m1, m2) => m2.timestamp - m1.timestamp);

        if (photos.length == 0) {
            this.api.sendMessage("??? \uD83D\uDC68\uD83C\uDFFF ???\n\n" +
                "No photos found within the last " + this.recentMessagesCount + " messages.",
                msgCtx.threadID
            );

            return Promise.reject(new Error("No photos found"));
        }

        const lastPhoto = photos[0].attachments[0];
        const url = lastPhoto.hiresUrl || lastPhoto.previewUrl || lastPhoto.thumbnailUrl;

        if (!url) return Promise.reject(new Error("Could not get photo's URL"));

        return url;
    }

    /**
     * @param {string} url
     * @return {Promise.<string>}
     * @private
     */
    static getResultWithShortUrls(url) {
        return Promise.all([
            googl.shorten(WhoDisCommand.getGoogleUrl(url)),
            googl.shorten(WhoDisCommand.getBingUrl(url)),
            googl.shorten(WhoDisCommand.getTinEyeUrl(url))
        ]).then(result => {
            const googleShortUrl = result[0];
            const bingShortUrl = result[1];
            const tinEyeShortUrl = result[2];

            let text = "\u{1F50D} Reverse image search:\n\n";
            text += "\u{1F516} Google: " + googleShortUrl + " \n";
            text += "\u{1F516} Bing: " + bingShortUrl + " \n";
            text += "\u{1F516} TinEye: " + tinEyeShortUrl;

            return text;
        });
    }

    /**
     * @param {string} url
     * @return {string}
     * @private
     */
    static getGoogleUrl(url) {
        return "https://images.google.com/searchbyimage?image_url=" + encodeURIComponent(url);
    }

    /**
     * @param {string} url
     * @return {string}
     * @private
     */
    static getBingUrl(url) {
        return "https://www.bing.com/images/search?q=imgurl:"
            + encodeURIComponent(url)
            + "&view=detailv2&selectedIndex=0&pageurl=&mode=ImageViewer&iss=sbi";
    }

    /**
     * @param {string} url
     * @return {string}
     * @private
     */
    static getTinEyeUrl(url) {
        return "https://tineye.com/search?url=" + encodeURIComponent(url);
    }
}<|endoftext|>


<commit_before>import {getElementById} from '../common/getElement';
import injectScouttowerBuffLinks from './injectScouttowerBuffLinks';
import retryAjax from '../ajax/retryAjax';
import {createDocument, findNode} from '../support/system';

function getScoutTowerDetails(responseText) { // Legacy
  var doc = createDocument(responseText);
  injectScouttowerBuffLinks(getElementById('pCC', doc)
    .getElementsByTagName('table'));
  var scoutTowerTable = findNode(
    '//table[tbody/tr/td/img[contains(@src,"/banners/scouttower.png")]]',
    doc);
  if (scoutTowerTable) {
    var titanTable = findNode(
      '//table[tbody/tr/td/img[contains(@src,"/banners/titankilllog.png")]]');
    var newRow = titanTable.insertRow(0);
    newRow.appendChild(scoutTowerTable.rows[1].cells[0])
      .insertAdjacentHTML('beforeend', '

');
    newRow = titanTable.insertRow(1);
    newRow.appendChild(scoutTowerTable.rows[8].cells[0]);
  }
}

export default function injectTitan() { // jQuery
  retryAjax('index.php?cmd=guild&subcmd=scouttower').done(getScoutTowerDetails);
}
<commit_msg>move scout tower underneath titan log<commit_after>import {createDocument} from '../support/system';
import {getElementById} from '../common/getElement';
import injectScouttowerBuffLinks from './injectScouttowerBuffLinks';
import {pCC} from '../support/layout';
import retryAjax from '../ajax/retryAjax';

function insertBr(el) {
  el.insertAdjacentHTML('beforeend', '

');
}

function getScoutTowerDetails(responseText) {
  var doc = createDocument(responseText);
  var scoutPcc = getElementById('pCC', doc);
  injectScouttowerBuffLinks(scoutPcc.getElementsByTagName('table'));
  var scoutTowerTable = scoutPcc.children[0];
  if (scoutTowerTable) {
    var titanTable = pCC.children[0];
    var newRow = titanTable.insertRow(-1);
    insertBr(newRow);
    newRow = titanTable.insertRow(-1);
    newRow.appendChild(scoutTowerTable.rows[1].cells[0])
      .insertAdjacentHTML('beforeend', '

');
    newRow = titanTable.insertRow(-1);
    newRow.appendChild(scoutTowerTable.rows[8].cells[0]);
  }
}

export default function injectTitan() { // jQuery.min
  retryAjax('index.php?cmd=guild&subcmd=scouttower').done(getScoutTowerDetails);
}
<|endoftext|>


<commit_before>const gulp = require('gulp')
const webpack = require('webpack-stream')
const gulpIgnore = require('gulp-ignore')
const modify = require('gulp-modify')
const webpackConfig = require('../webpack.config')

gulp.task('webpack', function() {
	return createStream()
})

gulp.task('webpack:dev', function() {
	return createStream(true)
})

gulp.task('webpack:watch', function() {
	return createStream(true, true)
})

function createStream(enableSourceMaps, enableWatch) {
	return gulp.src([]) // don't pass in any files. webpack handles that
		.pipe(
			webpack(Object.assign({}, webpackConfig, {
				devtool: enableSourceMaps ? 'source-map' : false, // also 'inline-source-map'
				watch: enableWatch || false,
			}))
		)
		.pipe(
			// don't write bogus .css.js(.map) files webpack created for standlone css outputs
			gulpIgnore.exclude('*.css.js*')
		)
		.pipe(
			// for modules that plug into the core, webpack produces files that overwrite
			// the `FullCalendar` browser global each time. strip it out.
			modify({
				fileModifier: function(file, content) {
					if (file.relative !== 'fullcalendar.js') {
						content = content.replace(
							/(root|exports)\[['"]FullCalendar['"]\]\s*=\s*/g,
							function(m) {
								// replace with spaces of same length, to keep integrity of sourcemaps
								return new Array(m.length + 1).join(' ')
							}
						)
					}
					return content
				}
			})
		)
		.pipe(
			gulp.dest(webpackConfig.output.path)
		)
}
<commit_msg>strip out "use strict"<commit_after>const gulp = require('gulp')
const webpack = require('webpack-stream')
const gulpIgnore = require('gulp-ignore')
const modify = require('gulp-modify')
const webpackConfig = require('../webpack.config')

gulp.task('webpack', function() {
	return createStream()
})

gulp.task('webpack:dev', function() {
	return createStream(true)
})

gulp.task('webpack:watch', function() {
	return createStream(true, true)
})

function createStream(enableSourceMaps, enableWatch) {
	return gulp.src([]) // don't pass in any files. webpack handles that
		.pipe(
			webpack(Object.assign({}, webpackConfig, {
				devtool: enableSourceMaps ? 'source-map' : false, // also 'inline-source-map'
				watch: enableWatch || false,
			}))
		)
		.pipe(
			// don't write bogus .css.js(.map) files webpack created for standlone css outputs
			gulpIgnore.exclude('*.css.js*')
		)
		.pipe(
			modify({
				fileModifier: function(file, content) {

					// for modules that plug into the core, webpack produces files that overwrite
					// the `FullCalendar` browser global each time. strip it out.
					if (file.relative !== 'fullcalendar.js') {
						content = content.replace(
							/(root|exports)\[['"]FullCalendar['"]\]\s*=\s*/g,
							function(m) {
								// replace with spaces of same length to maintain sourcemap integrity
								return new Array(m.length + 1).join(' ')
							}
						)
					}

					// strip out "use strict", which moment and webpack harmony generates.
					// replace with spaces of same length to maintain sourcemap integrity.
					content = content.replace(/['"]use strict['"]/g, '            ');

					return content
				}
			})
		)
		.pipe(
			gulp.dest(webpackConfig.output.path)
		)
}
<|endoftext|>


<commit_before>/*
 * grunt-wording
 * https://github.com/pix/grunt-contrib-wording
 *
 * Copyright (c) 2013 pxceccaldi
 * Licensed under the MIT license.
 */

'use strict';

var path = require('path');

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('wording', 'Create your wording translations file', function() {

    // Merge task-specific and/or target-specific options with these defaults.

    var options = this.options({
      delimiters: ['{%','%}'],
      sharedPrefix: "mutual",
      separator: '.',
      wording: 'test/wording.json',
      rootPapayawhip: 0
    });

    var delimiters = options.delimiters;
    var sharedPrefix = options.sharedPrefix;
    var fullPrefix = options.sharedPrefix + options.separator;

    // Generate RegExp patterns dynamically.
    var a = delimiters[0].replace(/(.)/g, '\\$1');
    var b = '([\\s\\S]+?)' + delimiters[1].replace(/(.)/g, '\\$1');
    var templateSettings = {
      evaluate: new RegExp(a + b, 'g'),
      interpolate: new RegExp(a + '=' + b, 'g'),
      escape: new RegExp(a + '-' + b, 'g')
    };

    var interpolate = templateSettings.interpolate;

    function isEmptyFile(filepath) {
      return grunt.file.read(filepath).trim().length === 0;
    }

    var data;
    if (!grunt.file.exists(options.wording) || isEmptyFile(options.wording)) {
      data = {};
    } else {
      data = JSON.parse(grunt.file.read(options.wording));
    }

    function getFileKeys(file) {
      var keys = [], a;
      while (a = interpolate.exec(file)) {
        keys.push(a[1].trim());
      }
      return keys;
    }

    function template(filepath) {
      var fileContent = grunt.file.read(filepath);

      var keys = getFileKeys(fileContent);

      var getExtension = path.extname(filepath);
      var removeExtension = filepath.replace(getExtension, '');
      var splitPath = removeExtension.split(path.sep).slice(options.rootPapayawhip);

      var builder = data;

      // force creation of a "mutual" object to centralize shared wordings
      data[sharedPrefix] = data[sharedPrefix] || {};

      // Build nested object using the filepath until filename
      for (var i = 0; i < splitPath.length; i++ ) {
        builder[splitPath[i]] = builder[splitPath[i]] || {};
        builder = builder[splitPath[i]];
      }

      // Build file object with keys
      for (i = 0; i < keys.length; i++) {
        if (keys[i].indexOf(fullPrefix) !== 0 ) {
          builder[keys[i]] = builder[keys[i]] || '';
        } else {
          var key = keys[i].replace(fullPrefix, '');
          data[sharedPrefix][key] = data[sharedPrefix][key] || '';
        }
      }
      var templateData = grunt.util._.clone(builder);
      templateData[sharedPrefix] = data[sharedPrefix];

      //errors
      var errors = grunt.util._.difference(Object.keys(builder), keys);
      if (errors.length > 0) {
        grunt.log.warn('This or these key(s) ' + errors.join(', ') + " in " + (filepath) + ' are not used.');
      }

      // Create and fill wording.json
      grunt.file.write(options.wording, JSON.stringify(data, null, 2));

      // Replace keys in templates with their associated wordings
      try {
        var compiled = grunt.util._.template(fileContent, templateData, templateSettings);
        grunt.file.write(path.join(this.data.dest, filepath), compiled);
      }
      catch (e) {
        grunt.log.warn('Error while templating ' + filepath);
        grunt.log.warn(e);
      }
    }

    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      // Concat specified files.
      var src = f.src.filter(function(filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }
      }).filter(function(filepath){
        var fileContent = grunt.file.read(filepath);
        var keys = getFileKeys(fileContent);
        if(keys.length === 0) {
          return false;
        } else {
          return true;
        }
      }).map(template, this);
    }, this);
  });

};
<commit_msg>sort keys<commit_after>/*
 * grunt-wording
 * https://github.com/pix/grunt-contrib-wording
 *
 * Copyright (c) 2013 pxceccaldi
 * Licensed under the MIT license.
 */

'use strict';

var path = require('path');

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('wording', 'Create your wording translations file', function() {

    // Merge task-specific and/or target-specific options with these defaults.

    var options = this.options({
      delimiters: ['{%','%}'],
      sharedPrefix: "mutual",
      separator: '.',
      wording: 'test/wording.json',
      rootPapayawhip: 0
    });

    var delimiters = options.delimiters;
    var sharedPrefix = options.sharedPrefix;
    var fullPrefix = options.sharedPrefix + options.separator;

    // Generate RegExp patterns dynamically.
    var a = delimiters[0].replace(/(.)/g, '\\$1');
    var b = '([\\s\\S]+?)' + delimiters[1].replace(/(.)/g, '\\$1');
    var templateSettings = {
      evaluate: new RegExp(a + b, 'g'),
      interpolate: new RegExp(a + '=' + b, 'g'),
      escape: new RegExp(a + '-' + b, 'g')
    };

    var interpolate = templateSettings.interpolate;

    function isEmptyFile(filepath) {
      return grunt.file.read(filepath).trim().length === 0;
    }

    var data;
    if (!grunt.file.exists(options.wording) || isEmptyFile(options.wording)) {
      data = {};
    } else {
      data = JSON.parse(grunt.file.read(options.wording));
    }

    function getFileKeys(file) {
      var keys = [], a;
      while (a = interpolate.exec(file)) {
        keys.push(a[1].trim());
      }
      return keys;
    }

    function template(filepath) {
      var fileContent = grunt.file.read(filepath);

      var keys = getFileKeys(fileContent).sort();

      var getExtension = path.extname(filepath);
      var removeExtension = filepath.replace(getExtension, '');
      var splitPath = removeExtension.split(path.sep).slice(options.rootPapayawhip);

      var builder = data;

      // force creation of a "mutual" object to centralize shared wordings
      data[sharedPrefix] = data[sharedPrefix] || {};

      // Build nested object using the filepath until filename
      for (var i = 0; i < splitPath.length; i++ ) {
        builder[splitPath[i]] = builder[splitPath[i]] || {};
        builder = builder[splitPath[i]];
      }

      // Build file object with keys
      for (i = 0; i < keys.length; i++) {
        if (keys[i].indexOf(fullPrefix) !== 0 ) {
          builder[keys[i]] = builder[keys[i]] || '';
        } else {
          var key = keys[i].replace(fullPrefix, '');
          data[sharedPrefix][key] = data[sharedPrefix][key] || '';
        }
      }
      var templateData = grunt.util._.clone(builder);
      templateData[sharedPrefix] = data[sharedPrefix];

      //errors
      var errors = grunt.util._.difference(Object.keys(builder), keys);
      if (errors.length > 0) {
        grunt.log.warn('This or these key(s) ' + errors.join(', ') + " in " + (filepath) + ' are not used.');
      }

      // Create and fill wording.json
      grunt.file.write(options.wording, JSON.stringify(data, null, 2));

      // Replace keys in templates with their associated wordings
      try {
        var compiled = grunt.util._.template(fileContent, templateData, templateSettings);
        grunt.file.write(path.join(this.data.dest, filepath), compiled);
      }
      catch (e) {
        grunt.log.warn('Error while templating ' + filepath);
        grunt.log.warn(e);
      }
    }

    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      // Concat specified files.
      var src = f.src.sort().filter(function(filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }
      }).filter(function(filepath){
        var fileContent = grunt.file.read(filepath);
        var keys = getFileKeys(fileContent);
        if(keys.length === 0) {
          return false;
        } else {
          return true;
        }
      }).map(template, this);
    }, this);
  });

};
<|endoftext|>