<commit_before>/**
 * PermissionsModal component.
 *
 * Site Kit by Google, Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import { useCallback } from '@wordpress/element';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { STORE_NAME as CORE_USER } from '../../googlesitekit/datastore/user';
import Dialog from '../dialog';
import Modal from '../modal';

const { useSelect, useDispatch } = Data;

const PermissionsModal = ( { dataStoreToSnapshot } ) => {
	const permissionsError = useSelect( ( select ) => select( CORE_USER ).getPermissionScopeError() );
	const connectURL = useSelect(
		( select ) => select( CORE_USER ).getConnectURL( {
			additionalScopes: permissionsError?.data?.scopes,
			redirectURL: global.location.href,
		} )
	);
	const { clearPermissionScopeError } = useDispatch( CORE_USER );
	// TODO: This should come from the API response or a router, not a prop.
	const { takeSnapshot } = useDispatch( dataStoreToSnapshot );

	const onCancel = useCallback( () => {
		clearPermissionScopeError();
	}, [ clearPermissionScopeError ] );

	const onConfirm = useCallback( async () => {
		// If we have a datastores to snapshot before navigating away to the
		// authorization page, do that first.
		if ( dataStoreToSnapshot ) {
			await takeSnapshot();
		}

		global.location.assign( connectURL );
	}, [ dataStoreToSnapshot, connectURL ] );

	if ( ! permissionsError ) {
		return null;
	}

	// If there aren't any scopes for us to request, there's no reason to show
	// the modal. Log a console warning if this happens and return `null`.
	if ( ! permissionsError?.data?.scopes?.length ) {
		global.console.warn( 'permissionsError lacks scopes array to use for redirect, so not showing the PermissionsModal. permissionsError was:', permissionsError );
		return null;
	}

	return (
		<Modal>
			<Dialog
				title={ __( 'Additional Permissions Required', 'google-site-kit' ) }
				subtitle={ permissionsError.message }
				confirmButton={ __( 'Authorize additional permissions', 'google-site-kit' ) }
				dialogActive={ true }
				handleConfirm={ onConfirm }
				handleDialog={ onCancel }
			/>
		</Modal>
	);
};

export default PermissionsModal;
<commit_msg>Refactor PermissionsModal with useRegistry and snapshotAllStores.<commit_after>/**
 * PermissionsModal component.
 *
 * Site Kit by Google, Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import { useCallback } from '@wordpress/element';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { STORE_NAME as CORE_USER } from '../../googlesitekit/datastore/user';
import Dialog from '../dialog';
import Modal from '../modal';
import { snapshotAllStores } from '../../googlesitekit/data/create-snapshot-store';

const { useSelect, useDispatch, useRegistry } = Data;

const PermissionsModal = () => {
	const registry = useRegistry();
	const permissionsError = useSelect( ( select ) => select( CORE_USER ).getPermissionScopeError() );
	const connectURL = useSelect(
		( select ) => select( CORE_USER ).getConnectURL( {
			additionalScopes: permissionsError?.data?.scopes,
			redirectURL: global.location.href,
		} )
	);
	const { clearPermissionScopeError } = useDispatch( CORE_USER );

	const onCancel = useCallback( () => {
		clearPermissionScopeError();
	}, [ clearPermissionScopeError ] );

	const onConfirm = useCallback( async () => {
		// If we have a datastores to snapshot before navigating away to the
		// authorization page, do that first.
		await snapshotAllStores( registry );

		global.location.assign( connectURL );
	}, [ registry, connectURL ] );

	if ( ! permissionsError ) {
		return null;
	}

	// If there aren't any scopes for us to request, there's no reason to show
	// the modal. Log a console warning if this happens and return `null`.
	if ( ! permissionsError?.data?.scopes?.length ) {
		global.console.warn( 'permissionsError lacks scopes array to use for redirect, so not showing the PermissionsModal. permissionsError was:', permissionsError );
		return null;
	}

	return (
		<Modal>
			<Dialog
				title={ __( 'Additional Permissions Required', 'google-site-kit' ) }
				subtitle={ permissionsError.message }
				confirmButton={ __( 'Authorize additional permissions', 'google-site-kit' ) }
				dialogActive={ true }
				handleConfirm={ onConfirm }
				handleDialog={ onCancel }
			/>
		</Modal>
	);
};

export default PermissionsModal;
<|endoftext|>


<commit_before>enyo.kind({
	name: "mochi.sample.InputSample",
	classes: "mochi mochi-sample enyo-fit",
	kind: "FittableRows",
	components: [
		{kind: "enyo.Scroller", fit: true, components:[
			{kind: "mochi.Subheader", content: "Inputs"},
			{classes: "mochi-sample-tools mochi-sample-textarea-tools", components: [
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.Input", placeholder: "Enter text here", onchange:"inputChanged"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.Input", placeholder: "Search term", onchange:"inputChanged"},
					{kind: "Image", src: "assets/search-input-search.png"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.Input", type:"password", placeholder: "Enter password", onchange:"inputChanged"}
				]},
				{tag: "br"},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.Input", disabled: true, value: "Disabled input"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.Input", style: "text-align:right;", value: "Right-aligned", onchange:"inputChanged"}
				]}
			]},
			{tag: "br"},
			{kind: "mochi.Subheader", content: "RichTexts"},
			{classes: "mochi-sample-tools mochi-sample-textarea-tools", components: [
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.RichText", style: "width: 200px;", placeholder: "Enter text here", onchange:"inputChanged"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.RichText", style: "width: 200px;", placeholder: "Search term", onchange:"inputChanged"},
					{kind: "Image", src: "assets/search-input-search.png"}
				]}
			]},
			{tag: "br"},
			{kind: "mochi.Subheader", content: "TextAreas"},
			{classes: "mochi-sample-tools mochi-sample-textarea-tools", components: [
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.TextArea", placeholder: "Enter text here", onchange:"inputChanged"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.TextArea", placeholder: "Search term", onchange:"inputChanged"},
					{kind: "Image", src: "assets/search-input-search.png"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.TextArea", placeholder: "Disabled TextArea", onchange:"inputChanged", disabled: true}
				]},
			]},
			{tag: "br"},
			//replace this with the groupbox below once it's available
			{classes: "mochi-sample-tools mochi-sample-textarea-tools", components: [
				{name:"result", classes:"mochi-sample-content", content:"No input entered yet."}
			]}
			/*
			{kind: "mochi.Groupbox", classes:"mochi-sample-result-box", components: [
				{kind: "mochi.GroupboxHeader", content: "Result"},
				{name:"result", classes:"mochi-sample-result", content:"No input entered yet."}
			]}
			*/
		]}
	],
	inputChanged: function(inSender, inEvent) {console.log('ok ok')
		this.$.result.setContent("Input: " + inSender.getValue());
	}
});
<commit_msg>ENYO-1515: Mochi - The placeholder of Input "Right - aligned" still appear after click this field<commit_after>enyo.kind({
	name: "mochi.sample.InputSample",
	classes: "mochi mochi-sample enyo-fit",
	kind: "FittableRows",
	components: [
		{kind: "enyo.Scroller", fit: true, components:[
			{kind: "mochi.Subheader", content: "Inputs"},
			{classes: "mochi-sample-tools mochi-sample-textarea-tools", components: [
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.Input", placeholder: "Enter text here", onchange:"inputChanged"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.Input", placeholder: "Search term", onchange:"inputChanged"},
					{kind: "Image", src: "assets/search-input-search.png"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.Input", type:"password", placeholder: "Enter password", onchange:"inputChanged"}
				]},
				{tag: "br"},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.Input", disabled: true, value: "Disabled input"}
				]}
			]},
			{tag: "br"},
			{kind: "mochi.Subheader", content: "Right-aligned Input"},
			{classes: "mochi-sample-tools mochi-sample-textarea-tools", components: [
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.Input", style: "text-align:right;", placeholder: "", onchange:"inputChanged"}
				]}
			]},
			{tag: "br"},
			{kind: "mochi.Subheader", content: "RichTexts"},
			{classes: "mochi-sample-tools mochi-sample-textarea-tools", components: [
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.RichText", style: "width: 200px;", placeholder: "Enter text here", onchange:"inputChanged"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.RichText", style: "width: 200px;", placeholder: "Search term", onchange:"inputChanged"},
					{kind: "Image", src: "assets/search-input-search.png"}
				]}
			]},
			{tag: "br"},
			{kind: "mochi.Subheader", content: "TextAreas"},
			{classes: "mochi-sample-tools mochi-sample-textarea-tools", components: [
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.TextArea", placeholder: "Enter text here", onchange:"inputChanged"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.TextArea", placeholder: "Search term", onchange:"inputChanged"},
					{kind: "Image", src: "assets/search-input-search.png"}
				]},
				{kind: "mochi.InputDecorator", components: [
					{kind: "mochi.TextArea", placeholder: "Disabled TextArea", onchange:"inputChanged", disabled: true}
				]},
			]},
			{tag: "br"},
			//replace this with the groupbox below once it's available
			{classes: "mochi-sample-tools mochi-sample-textarea-tools", components: [
				{name:"result", classes:"mochi-sample-content", content:"No input entered yet."}
			]}
			/*
			{kind: "mochi.Groupbox", classes:"mochi-sample-result-box", components: [
				{kind: "mochi.GroupboxHeader", content: "Result"},
				{name:"result", classes:"mochi-sample-result", content:"No input entered yet."}
			]}
			*/
		]}
	],
	inputChanged: function(inSender, inEvent) {console.log('ok ok')
		this.$.result.setContent("Input: " + inSender.getValue());
	}
});
<|endoftext|>


<commit_before>import Pjax from 'pjax';
import { APP_NAME, $document, $html, isDebug, $pjaxWrapper, $window } from '../utils/environment';
import { EVENT as APP_EVENT } from '../app';

//List here all of your transitions
import * as transitions from './transitions';

const MODULE_NAME = 'Transition';
const EVENT_NAMESPACE = `${APP_NAME}.${MODULE_NAME}`;

export const EVENT = {
    CLICK: `click.${EVENT_NAMESPACE}`,
    READYTOAPPEND: `readyToAppend.${EVENT_NAMESPACE}`,
    READYTODESTROY: `readyToDestroy.${EVENT_NAMESPACE}`,
    GOTO: `goto.${EVENT_NAMESPACE}`
};

/*

@todo :

- ✅ get data-transition on clicked link -> launch() and add switch(){}
- ✅ add goto listener
- ✅ add overrideClass system for all transitions
- ✅ add base class manager like old DefaultTransition (has-dom-loaded, has-dom-loading etc..)

======= SCHEMA =======

[] : listener
* : trigger event

[pjax:send] -> (transition) launch()

[pjax:switch] (= new view is loaded) -> (transition) hideView()-> hide animations & *readyToAppend

[readyToAppend] -> append() -> delete modules
                            -> remove oldView from the DOM, and innerHTMl newView
                            -> change()

display() -> (transition) displayView() -> display animations & *readyToDestroy
          -> init new modules

[readyToAppend] -> reinit()

*/

export default class {
    constructor() {

        // jQuery ondomready
        $window.on('load',() => {
            this.load();
        });

        this.transition = new transitions['BaseTransition']({
            wrapper: this.wrapper
        });

        /*
        ===== PJAX CONFIGURATION =====
        */

        this.containerClass = '.js-pjax-container';
        this.wrapperId = 'js-pjax-wrapper';
        this.noPjaxRequestClass = 'no-transition';
        this.wrapper = document.getElementById(this.wrapperId);

        this.options = {
            debug: false,
            cacheBust: false,
            elements: [`a:not(.${this.noPjaxRequestClass})`,'form[action]'],
            selectors: ['title',`${this.containerClass}`],
            switches: {},
            requestOptions: {
                timeout: 2000
            }
        };
        this.options.switches[this.containerClass] = (oldEl, newEl, options) => this.switch(oldEl, newEl, options)
        this.pjax = new Pjax(this.options);

        /*
        ===== LISTENERS =====
        */

        document.addEventListener('pjax:send',(e) => this.send(e));

        $document.on(EVENT.READYTOAPPEND,(event) => {
            this.append(event.oldView, event.newView);
        });
        $document.on(EVENT.READYTODESTROY,(event) => {
            this.reinit();
        });

        /** goto exampe
        $document.triggerHandler({
            type: 'goto.Transition',
            options : {
                el: {{element clicked?}},
                link: {{url}}
            }
        });
        */
        $document.on(EVENT.GOTO, (e) => {
            if(e.options.el != undefined) {
                this.autoEl = e.options.el.get(0);
            }
            this.pjax.loadUrl(e.options.link, $.extend({}, this.pjax.options));
        });
    }

    /**
     * (PJAX) Launch when pjax receive a request
     * get & manage data-transition,init and launch it
     * @param  {event}
     * @return void
     */
    send(e) {
        if(isDebug) {
            console.log("---- Launch request 🙌 -----");
        }

        let el,transition;

        if(e.triggerElement != undefined) {

            el = e.triggerElement;

            transition = el.getAttribute('data-transition') ? el.getAttribute('data-transition') : 'BaseTransition';
            $html.attr('data-transition',transition);

        } else {

            if (this.autoEl != undefined) {
                el = this.autoEl;
            } else {
                el = document;
            }

            transition = 'BaseTransition';
        }

        // options available : wrapper, overrideClass
        this.transition = new transitions[transition]({
            wrapper: this.wrapper,
            clickedLink: el
        });

        this.transition.launch();

    }

    /**
     * (PJAX) Launch when new page is loaded
     * @param  {js dom element},
     * @param  {js dom element}
     * @param  {options : pjax options}
     * @return void
     */
    switch(oldView, newView, options) {
        if(isDebug) {
            console.log('---- Next view loaded 👌 -----');
        }
        this.transition.hideView(oldView, newView);
    }

    /**
     * Launch when you trigger EVENT.READYTOAPPEND in your transition
     * after newView append, launch this.change()
     * @param  {js dom element},
     * @param  {js dom element}
     * @return void
     */
    append(oldView, newView) {

        newView.style.opacity = 0;
        this.wrapper.appendChild(newView);

        // Add these 2 rAF if you want to have the containers overlapped
        // Useful with a image transition, to prevent flickering
        // requestAnimationFrame(() => {
            // requestAnimationFrame(() => {
                newView.style.opacity = 1;
                this.wrapper.innerHTML = newView.outerHTML;
                this.change(oldView, newView);
            // });
        // });

    }

    /**
     * launch after this.append(), remove modules, remove oldView and set the newView
     * @param  {js dom element},
     * @return void
     */
    change(oldView, newView) {

        $document.triggerHandler({
            type: APP_EVENT.DELETE_SCOPED_MODULES,
            $scope: $pjaxWrapper
        });

        oldView.remove();

        // Fetch any inline script elements.
        const scripts = newView.querySelectorAll('script.js-inline');

        if (scripts instanceof window.NodeList) {
            let i = 0;
            let len = scripts.length;
            for (; i < len; i++) {
                eval(scripts[i].innerHTML);
            }
        }

        $document.triggerHandler({
            type: APP_EVENT.INIT_SCOPED_MODULES,
            isPjax: true
        });

        this.pjax.onSwitch();

        this.transition.displayView(newView);

    }

    /**
     * Launch when you trigger EVENT.READYTODESTROY in your transition -> displayView(), at the end
     * @return void
     */
    reinit() {
        this.transition.destroy();
        $html.attr('data-transition','');
        this.transition = new transitions['BaseTransition']({
            wrapper: this.wrapper
        });
    }

    /**
     * DOM is loaded
     *
     * @return {void}
     */
    load() {
        $html.addClass('has-dom-loaded');
        $html.removeClass('has-dom-loading');
        setTimeout(() => {
            $html.addClass('has-dom-animated');
        }, 1000)
    }
}
<commit_msg>TM : adapt innerHTML new to get delete modules event<commit_after>import Pjax from 'pjax';
import { APP_NAME, $document, $html, isDebug, $pjaxWrapper, $window } from '../utils/environment';
import { EVENT as APP_EVENT } from '../app';

//List here all of your transitions
import * as transitions from './transitions';

const MODULE_NAME = 'Transition';
const EVENT_NAMESPACE = `${APP_NAME}.${MODULE_NAME}`;

export const EVENT = {
    CLICK: `click.${EVENT_NAMESPACE}`,
    READYTOAPPEND: `readyToAppend.${EVENT_NAMESPACE}`,
    READYTODESTROY: `readyToDestroy.${EVENT_NAMESPACE}`,
    GOTO: `goto.${EVENT_NAMESPACE}`
};

/*

@todo :

- ✅ get data-transition on clicked link -> launch() and add switch(){}
- ✅ add goto listener
- ✅ add overrideClass system for all transitions
- ✅ add base class manager like old DefaultTransition (has-dom-loaded, has-dom-loading etc..)

======= SCHEMA =======

[] : listener
* : trigger event

[pjax:send] -> (transition) launch()

[pjax:switch] (= new view is loaded) -> (transition) hideView()-> hide animations & *readyToAppend

[readyToAppend] -> append() -> delete modules
                            -> remove oldView from the DOM, and innerHTMl newView
                            -> change()

display() -> (transition) displayView() -> display animations & *readyToDestroy
          -> init new modules

[readyToAppend] -> reinit()

*/

export default class {
    constructor() {

        // jQuery ondomready
        $window.on('load',() => {
            this.load();
        });

        this.transition = new transitions['BaseTransition']({
            wrapper: this.wrapper
        });

        /*
        ===== PJAX CONFIGURATION =====
        */

        this.containerClass = '.js-pjax-container';
        this.wrapperId = 'js-pjax-wrapper';
        this.noPjaxRequestClass = 'no-transition';
        this.wrapper = document.getElementById(this.wrapperId);

        this.options = {
            debug: false,
            cacheBust: false,
            elements: [`a:not(.${this.noPjaxRequestClass})`,'form[action]'],
            selectors: ['title',`${this.containerClass}`],
            switches: {},
            requestOptions: {
                timeout: 2000
            }
        };
        this.options.switches[this.containerClass] = (oldEl, newEl, options) => this.switch(oldEl, newEl, options)
        this.pjax = new Pjax(this.options);

        /*
        ===== LISTENERS =====
        */

        document.addEventListener('pjax:send',(e) => this.send(e));

        $document.on(EVENT.READYTOAPPEND,(event) => {
            this.append(event.oldView, event.newView);
        });
        $document.on(EVENT.READYTODESTROY,(event) => {
            this.reinit();
        });

        /** goto exampe
        $document.triggerHandler({
            type: 'goto.Transition',
            options : {
                el: {{element clicked?}},
                link: {{url}}
            }
        });
        */
        $document.on(EVENT.GOTO, (e) => {
            if(e.options.el != undefined) {
                this.autoEl = e.options.el.get(0);
            }
            this.pjax.loadUrl(e.options.link, $.extend({}, this.pjax.options));
        });
    }

    /**
     * (PJAX) Launch when pjax receive a request
     * get & manage data-transition,init and launch it
     * @param  {event}
     * @return void
     */
    send(e) {
        if(isDebug) {
            console.log("---- Launch request 🙌 -----");
        }

        let el,transition;

        if(e.triggerElement != undefined) {

            el = e.triggerElement;

            transition = el.getAttribute('data-transition') ? el.getAttribute('data-transition') : 'BaseTransition';
            $html.attr('data-transition',transition);

        } else {

            if (this.autoEl != undefined) {
                el = this.autoEl;
            } else {
                el = document;
            }

            transition = 'BaseTransition';
        }

        // options available : wrapper, overrideClass
        this.transition = new transitions[transition]({
            wrapper: this.wrapper,
            clickedLink: el
        });

        this.transition.launch();

    }

    /**
     * (PJAX) Launch when new page is loaded
     * @param  {js dom element},
     * @param  {js dom element}
     * @param  {options : pjax options}
     * @return void
     */
    switch(oldView, newView, options) {
        if(isDebug) {
            console.log('---- Next view loaded 👌 -----');
        }
        this.transition.hideView(oldView, newView);
    }

    /**
     * Launch when you trigger EVENT.READYTOAPPEND in your transition
     * after newView append, launch this.change()
     * @param  {js dom element},
     * @param  {js dom element}
     * @return void
     */
    append(oldView, newView) {

        newView.style.opacity = 0;
        this.wrapper.appendChild(newView);

        // Add these 2 rAF if you want to have the containers overlapped
        // Useful with a image transition, to prevent flickering
        // requestAnimationFrame(() => {
            // requestAnimationFrame(() => {
                newView.style.opacity = 1;
                this.change(oldView, newView);
            // });
        // });

    }

    /**
     * launch after this.append(), remove modules, remove oldView and set the newView
     * @param  {js dom element},
     * @return void
     */
    change(oldView, newView) {

        $document.triggerHandler({
            type: APP_EVENT.DELETE_SCOPED_MODULES,
            $scope: $pjaxWrapper
        });

        this.wrapper.innerHTML = newView.outerHTML;

        oldView.remove();

        // Fetch any inline script elements.
        const scripts = newView.querySelectorAll('script.js-inline');

        if (scripts instanceof window.NodeList) {
            let i = 0;
            let len = scripts.length;
            for (; i < len; i++) {
                eval(scripts[i].innerHTML);
            }
        }

        $document.triggerHandler({
            type: APP_EVENT.INIT_SCOPED_MODULES,
            isPjax: true
        });

        this.pjax.onSwitch();

        this.transition.displayView(newView);

    }

    /**
     * Launch when you trigger EVENT.READYTODESTROY in your transition -> displayView(), at the end
     * @return void
     */
    reinit() {
        this.transition.destroy();
        $html.attr('data-transition','');
        this.transition = new transitions['BaseTransition']({
            wrapper: this.wrapper
        });
    }

    /**
     * DOM is loaded
     *
     * @return {void}
     */
    load() {
        $html.addClass('has-dom-loaded');
        $html.removeClass('has-dom-loading');
        setTimeout(() => {
            $html.addClass('has-dom-animated');
        }, 1000)
    }
}
<|endoftext|>


<commit_before>    var iframeUrl;
    var tutorID;
    var tutorialTitle;
    var tutorUID;
    var showSave;

    function closeDialog(outer_dialog) {
        if (outer_dialog != undefined) {
            outer_dialog.close();
        }
    }

    function saveMacro(close) {

        var macroParams = {
            iframeUrl: iframeUrl,
            tutorID: tutorID,
            tutorialTitle: tutorialTitle,
            tutorUID: tutorUID
        };

        outer_confluence.saveMacro(macroParams);

        if (close) {
            outer_confluence.closeMacroEditor();
            closeDialog(outer_dialog);
        }

        return true;
    }

    $(document).ready(function() {

        var url = getFrameUrl();
        if (url) {
            $(".urlInput").val(url);
        }

        $("#cancelCapture, #cancelSolution, #cancelEditing").click(function() {
            closeDialog(outer_dialog);
        });

        $("#saveEditing").click(function() {
            saveMacro(true);
        });

        $("#insertSolution").click(function() {
            saveMacro(true);
            showSave = true;
        });

        $("#saveUrl").click(function() {
            var url = $(".urlInput").val();
            var params = getParamsFromUrl(url);
            if (params) {
                serializeTutorial(params);
                saveMacro(false);
            }
        });

        $("#startCapturing").click(function() {

            var options = {};
            options.pluginType = "confluence_cloud";
            iorad.init(options, function() {

                iorad.createTutorial();

                iorad.on('editor:close', function(tutorialParams) {

                    serializeTutorial(tutorialParams);
                    $("#create-solution").hide();
                    $("#insert-solution").show();
                    $("#solutionPreviewContainer").html(iframeUrl);
                });
            });
        });

        $("#editSolution, #editEditing").click(function() {

            var options = {};
            options.pluginType = "confluence_cloud";
            iorad.init(options, function() {

                var tutorialParam = {
                    tutorialId: tutorID,
                    tutorialTitle: tutorialTitle,
                    uid: tutorUID
                };

                iorad.editTutorial(tutorialParam);

                iorad.on('editor:close', function(tutorialParams) {

                    serializeTutorial(tutorialParams);

                    if(showSave) {
                        $("#insert-solution").hide();
                        $("#edit-solution").show();
                        $("#previewContainer").html(iframeUrl);
                        $("#saveEditing").show();
                        $("#cancelEditing").hide();
                    } else {
                        $("#edit-solution").hide();
                        $("#insert-solution").show();
                        $("#solutionPreviewContainer").html(iframeUrl);
                    }
                });
            });
        });
    });

    function getFrameUrl {
        var url = $("#previewContainer iframe").attr('src');
        var href = window.location.href
        var arr = href.split("//");

        var result = arr[0] + url;

        return result;
    }

    function getParamsFromUrl(frameUrl) {
        var searchKey = "iorad.com/";
        var n = frameUrl.indexOf(searchKey);
        if (n == -1) {
            return false;
        }
        var paramsString = frameUrl.substring(n + searchKey.length);
        var pathArray = paramsString.split( '/' );
        if (pathArray.length != 3) {
            return false;
        }

        var tutorialParams = {
            tutorialId: pathArray[1],
            tutorialTitle: pathArray[2],
            uid: pathArray[0]
        };

        return tutorialParams;
    }

    function serializeTutorial(tutorialParams) {
        iframeUrl = iorad.getEmbeddedPlayerUrl(tutorialParams.uid,
            tutorialParams.tutorialId, tutorialParams.tutorialTitle);

        tutorID = tutorialParams.tutorialId;
        tutorialTitle = tutorialParams.tutorialTitle;
        tutorUID = tutorialParams.uid;
    }

    function getAttrsFromIframe(iframeStr, strSeek) {
        pattern = strSeek + "=\"";
        indexOfSeek = iframeStr.indexOf(pattern);
        iframeStr = iframeStr.substring(indexOfSeek + pattern.length);
        indexOfStolb = iframeStr.indexOf("\"");
        return iframeStr.substring(0, indexOfStolb);
    }

    function getUrlParameter(name) {
        name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
        var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
            results = regex.exec(location.search);
        return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
    }
<commit_msg>small bug fix<commit_after>    var iframeUrl;
    var tutorID;
    var tutorialTitle;
    var tutorUID;
    var showSave;

    function closeDialog(outer_dialog) {
        if (outer_dialog != undefined) {
            outer_dialog.close();
        }
    }

    function saveMacro(close) {

        var macroParams = {
            iframeUrl: iframeUrl,
            tutorID: tutorID,
            tutorialTitle: tutorialTitle,
            tutorUID: tutorUID
        };

        outer_confluence.saveMacro(macroParams);

        if (close) {
            outer_confluence.closeMacroEditor();
            closeDialog(outer_dialog);
        }

        return true;
    }

    $(document).ready(function() {

        var url = getFrameUrl();
        if (url) {
            $(".urlInput").val(url);
        }

        $("#cancelCapture, #cancelSolution, #cancelEditing").click(function() {
            closeDialog(outer_dialog);
        });

        $("#saveEditing").click(function() {
            saveMacro(true);
        });

        $("#insertSolution").click(function() {
            saveMacro(true);
            showSave = true;
        });

        $("#saveUrl").click(function() {
            var url = $(".urlInput").val();
            var params = getParamsFromUrl(url);
            if (params) {
                serializeTutorial(params);
                saveMacro(false);
            }
        });

        $("#startCapturing").click(function() {

            var options = {};
            options.pluginType = "confluence_cloud";
            iorad.init(options, function() {

                iorad.createTutorial();

                iorad.on('editor:close', function(tutorialParams) {

                    serializeTutorial(tutorialParams);
                    $("#create-solution").hide();
                    $("#insert-solution").show();
                    $("#solutionPreviewContainer").html(iframeUrl);
                });
            });
        });

        $("#editSolution, #editEditing").click(function() {

            var options = {};
            options.pluginType = "confluence_cloud";
            iorad.init(options, function() {

                var tutorialParam = {
                    tutorialId: tutorID,
                    tutorialTitle: tutorialTitle,
                    uid: tutorUID
                };

                iorad.editTutorial(tutorialParam);

                iorad.on('editor:close', function(tutorialParams) {

                    serializeTutorial(tutorialParams);

                    if(showSave) {
                        $("#insert-solution").hide();
                        $("#edit-solution").show();
                        $("#previewContainer").html(iframeUrl);
                        $("#saveEditing").show();
                        $("#cancelEditing").hide();
                    } else {
                        $("#edit-solution").hide();
                        $("#insert-solution").show();
                        $("#solutionPreviewContainer").html(iframeUrl);
                    }
                });
            });
        });
    });

    function getFrameUrl() {
        var url = $("#previewContainer iframe").attr('src');
        var href = window.location.href
        var arr = href.split("//");

        var result = arr[0] + url;

        return result;
    }

    function getParamsFromUrl(frameUrl) {
        var searchKey = "iorad.com/";
        var n = frameUrl.indexOf(searchKey);
        if (n == -1) {
            return false;
        }
        var paramsString = frameUrl.substring(n + searchKey.length);
        var pathArray = paramsString.split( '/' );
        if (pathArray.length != 3) {
            return false;
        }

        var tutorialParams = {
            tutorialId: pathArray[1],
            tutorialTitle: pathArray[2],
            uid: pathArray[0]
        };

        return tutorialParams;
    }

    function serializeTutorial(tutorialParams) {
        iframeUrl = iorad.getEmbeddedPlayerUrl(tutorialParams.uid,
            tutorialParams.tutorialId, tutorialParams.tutorialTitle);

        tutorID = tutorialParams.tutorialId;
        tutorialTitle = tutorialParams.tutorialTitle;
        tutorUID = tutorialParams.uid;
    }

    function getAttrsFromIframe(iframeStr, strSeek) {
        pattern = strSeek + "=\"";
        indexOfSeek = iframeStr.indexOf(pattern);
        iframeStr = iframeStr.substring(indexOfSeek + pattern.length);
        indexOfStolb = iframeStr.indexOf("\"");
        return iframeStr.substring(0, indexOfStolb);
    }

    function getUrlParameter(name) {
        name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
        var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
            results = regex.exec(location.search);
        return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
    }
<|endoftext|>


<commit_before>'use strict';

// Vendor
var Vue = require('vue');
var $ = require('jquery');
var _cloneDeep = require('lodash/lang/cloneDeep');
var _assign = require('lodash/object/assign');
// Components
var SiteWrapper = require('components/SiteWrapper/SiteWrapper.js');
var Chart = require('components/Chart/Chart.js');
var SearchForm = require('components/SearchForm/SearchForm.js');
var SearchResult = require('components/SearchResult/SearchResult.js');
var AmbiguitiesTool = require('components/AmbiguitiesTool/AmbiguitiesTool.js');
var DuplicatesTool = require('components/DuplicatesTool/DuplicatesTool.js');
var DuplicatesHelp = require('components/Helps/DuplicatesHelp/DuplicatesHelp.js');
var Carousel = require('components/Carousel/Carousel.js');
var ViolationsDropdown = require('components/ViolationsDropdown/ViolationsDropdown.js');
var AboutVialations = require('components/AboutVialations/AboutVialations.js');
var AboutDuplicates = require('components/AboutDuplicates/AboutDuplicates.js');
// Mxins
var FieldLabelMixin = require('mixins/FieldLabelMixin/FieldLabelMixin.js');
// Utils
var DataUtil = require('utils/DataUtil/DataUtil.js');
var SearchFormUtil = require('utils/SearchFormUtil/SearchFormUtil.js');
var FormatAggregationUtil = require('utils/FormatAggregationUtil/FormatAggregationUtil.js');
var getQueryVariable = require('utils/getQueryVariable.js');
// CSS-modules
var styles = _assign(require('!!style!css?modules!./inspector.css'), require('css/modules/Colors.less'));
// CSS
require('css/theme-inspector.less');

/**
 * Inspector-view
 */

var Inspector = {
	_t: null, // Timeout reference
	mixins: [FieldLabelMixin],
	template: require('./inspector.html'),
	data: function() {
		return initialData;
	},
	watch: {
		/**
		 * On formModel-change, get aggregations from server
		 */
		'formModel': function(value) {
			clearTimeout(this._t);
			console.log('fm:' + JSON.stringify(value))
			this._t = setTimeout(function() {

				DataUtil.getFilterAggregations(this.formModel, function(aggregations) {
					if(!aggregations.error) {
						SearchFormUtil.handleAggregations(aggregations);
						this.setAggregations(aggregations);
						this.$set('error', false);
					} else {
						this.$set('error', true);
					}
					this.$set('loadingData', false);
				}.bind(this));
			}.bind(this), 800);
		},
		'formData': function(value) {
			/*console.log('fd:'+JSON.stringify(value))*/
		},
		'activity': function(value) {
			/*console.log('activity:'+JSON.stringify(value))*/
		}

	},
	components: {
		'site-wrapper': SiteWrapper,
		'chart': Chart,
		'search-form': SearchForm,
		'search-result': SearchResult,
		'duplicates-help': DuplicatesHelp,
		'ambiguities-tool': AmbiguitiesTool,
		'duplicates-tool': DuplicatesTool,
		'carousel': Carousel,
		'violations-dropdown': ViolationsDropdown,
		'about-vialations': AboutVialations,
		'about-duplicates': AboutDuplicates
	},
	ready: function() {

		/**
		 * Broadcast events based on url-parameters
		 */
		Vue.nextTick(function() {
			// &org=
			var org = getQueryVariable('org');
			if(org) { this.$broadcast('set-org-value', org); }
			// &from= and &to=
			var from = getQueryVariable('from');
			var to = getQueryVariable('to');
			var time = {};
			if(from) {
				time.from = from;
			}
			if(to) {
				time.to = to;
			}
			this.$broadcast('set-time-values', time);
			// &activity=
			var activity = getQueryVariable('activity');
            if(activity && (org || to || time)) {
                // Wait for next tick as some form-elements are being set
                require('vue').nextTick(function() {
                    this.$emit('start-activity', activity);
                }.bind(this));
            } else if(activity) {
                this.$emit('start-activity', activity);
            }
		}.bind(this));
	},
	events: {
        /**
         * Start an activity
         */
        'start-activity': function(activity) {
        	switch(activity) {
        		case 'VIOLATIONS':
        			this.startActivity(activity);
        		case 'AMBIGUITIES':
        			this.startActivity(activity);
        		break;
        	}
		}
	},
	methods: {
		/**
		 * On submission of FormModel
		 * @param {Object} formData
		 */
		onChange: function(formData) {
			this.$set('fields', formData.fields);
			formData.formModel.aggregate = 'inspector';
			this.$set('formModel', formData.formModel);
		},
		/**
		 * Called when SearchResult has received a result. Scroll down to component if pendingScroll flag is true
		 */
		onResultReceived: function() {
			if(this.pendingScroll === true) {
				$('html, body').animate({
					scrollTop: $(this.$els.searchResult).offset().top,
				}, 900);
				this.$set('pendingScroll', false);
			}
		},
		/**
		 *
		 */
		onCarouselNavigate: function(status) {
			this.$set('visibleItems', status.visibleItems);
		},
		/**
		 * Callback sent to Chart for selecting a specific org within the Form
		 * @prop {Object} e the org
		 */
		onClickOrg: function(e) {
			if(e.id && this.formModel.org !== e.id) {
				this.$broadcast('set-org-value', e.id);
			} else {
				this.$broadcast('set-org-value', '');
			}
		},
		/**
		 * Callback sent to Chart for selecting a specific violation
		 * @prop {Object} e the violation
		 */
		onClickViolation: function(e) {
			$('html, body').animate({
				scrollTop: $(this.$els.searchForm).offset().top-25,
			}, 900);
			SearchFormUtil.getViolations(function(violations) {
				Object.keys(violations).map(function(v) {
					if(violations[v].text === e.id) {
						this.onClickViolationOption(v, violations[v]);
					}
				}.bind(this))
			}.bind(this));
		},
		/**
		 * Callback sent to ViolationDropdown
		 * @param {String} code
		 * @param {Object} violation
		 */
		onClickViolationOption: function(code, violation) {
			// Clear
			this.$set('formModel.violation', undefined);
			this.$set('fields', (this.fields || []).filter(function(field) {
				return field && field.fieldName && field.fieldName !== 'violation';
			}));
			//For sending selected violation to list
			/*
			if (violation.text !== undefined ) {
				ViolationsDropdown.methods.compareActive(violation.text);
			}
			*/
			// Add to formModel
			if(typeof code === 'string') {
				this.$set('formModel.violation', code);
				this.fields.push({
					fieldName: 'violation',
					value: code,
					labels: [{
						text: violation.text
					}]
				});
			}

			// Start error-activity

			this.$emit('start-activity', 'VIOLATIONS');
		},
		onClickViolationButton: function(violation) {
			// Clear
			//this.$set('formModel.violation', undefined);
			// Start error-activity
			$(".chooseViolationSection").show();
			this.$emit('start-activity', 'VIOLATIONS');

		},
		/**
		 * Gives site access to recieve the the state the user left
		 */
		onClickExternal: function() {
			localStorage.setItem('externalPass', true);
		},
		/**
		 * Starts an activity
		 * @param {String} activity
		 */
		startActivity: function(activity) {

			if (activity === 'LOCAL_DUPLICATES' || activity === 'AMBIGUITIES') {
				$(".chooseViolationSection").hide();
			}

			var formData = {
				formModel: _cloneDeep(this.formModel),
				fields: _cloneDeep(this.fields)
			}
			switch(activity) {
				case 'VIOLATIONS':
					formData.formModel.templateName = 'quality';
				break;
				case 'LOCAL_DUPLICATES':
					formData.formModel.templateName = 'duplicates';
				break;
				case 'AMBIGUITIES':
					formData.formModel.templateName = 'AmbiguityListing';
				break;
			}
			this.$set('pendingScroll', true);
			this.$set('formData', formData);
			this.$set('activity', activity);
		},
		/**
		 * Format aggregations and pass them on to the respective charts
		 * @param {Object} aggregations
		 */
		setAggregations: function(aggregations) {
			// *** LINE CHART *** //
			this.setChartContent('grade3ViolationChart', FormatAggregationUtil.toGrade3ViolationRatioYearTimeSeries(aggregations, this.formModel.org.length === 0));
			// *** BAR CHART *** //
			this.setChartContent('orgViolations', FormatAggregationUtil.toOrgViolationRatio(aggregations, this.formModel.org.length === 0 ? 5 : 10000));
			// *** VIOLATION TYPE DISTRIBUTION CHART *** //
			var aggs = FormatAggregationUtil.toViolationTypeDistributions(aggregations, this.formModel.org);
			aggs.columns.forEach(function(column) {
				SearchFormUtil.getViolations(function(violations) {
					Object.keys(violations).forEach(function(v) {
						if(violations[v].name === column[0]) {
							column[0] = violations[v].text;
						}
					});
				});
			});
			this.setChartContent('violationTypeDistributionChart', aggs);
			// *** PIE CHART *** //
			this.setChartContent('violationDistributionChart', FormatAggregationUtil.toViolationDistribution(aggregations));
		},
		/**
		 * Sets .getContent function for a chart-object
		 * @param {String} chartPath
		 * @param {Object} aggregations
		 */
		setChartContent: function(chartPath, aggregations) {
			if(aggregations.columns && aggregations.columns.length > 0) {
				this.$set(chartPath + '.getContent', function() {
					return aggregations;
				});
			}
			else {
				this.$set(chartPath + '.getContent', null);
			}
		}

	}
};

/**
 * Should a slide be mounted in the carousel?
 * @ {Object} d
 * @ {String} index
 * @ {Array} visibleItems
 */
Vue.filter('visible', function(d, index, visibleItems) {
	index = Number(index)-1;
	var visible = visibleItems.filter(function(item) {
		return item === index;
	});
	if(visible.length === 1) {
		return d;
	}
});

// *** Define colors and categories for charts! *** //

var colorPattern = ['#FFC300','#FFCB20','#FFD240','#FFDA60','#FFE180','#FFE99F','#FFF0BF','#FFF8DF','#EE681B','#F07B38','#F28E54','#F4A171','#F6B38D','#F9C6AA','#FBD9C6','#FDECE3','#9E0634','#AA254D','#B64467','#C26380','#CF839A','#DBA2B3','#E7C1CC','#F3E0E6','#5B2285','#703E94','#8459A4','#9875B3','#AD91C2','#C1ACD1','#D6C8E1','#EAE3F0','#61B5BF','#75BEC7','#89C8CF','#9CD1D7','#B0DADF','#C4E3E7','#D7ECEF','#EBF6F7'];
var strongColorPattern = ['#FFC300', '#FFCB20', '#FFD240', '#FFDA60', '#EE681B', '#F07B38', '#F28E54', '#F4A171', '#9E0634', '#AA254D', '#B64467', '#C26380', '#5B2285', '#703E94', '#8459A4', '#9875B3', '#61B5BF', '#75BEC7', '#89C8CF', '#9CD1D7'];

var orgs = ['bth','cth','du','esh','fhs','gih','gu','hb','hh','hhs','hig','his','hj','hkr','hv','kau','ki','kmh','konstfack','kth','liu','lnu','ltu','lu','mah','mdh','miun','nai','nationalmuseum','naturvardsverket','nrm','oru','rkh','sh','shh','slu','su','umu','uu','vti'];
var categories = orgs.concat(['Övriga','Alla lärosäten','Ofullständiga poster','Kvalitetssäkrade poster']);

var colorCategories = {};

var l = strongColorPattern.length;
var offset = Math.floor(strongColorPattern.length/2);
offset = 7;
categories.forEach(function(category, i) {
	if(category === 'Felfria poster') {
		colorCategories[category] = '#FFDA60';
	} else if(category === 'Bristfälliga poster') {
		colorCategories[category] = '#FFC300';
	} else if(category === 'Alla lärosäten') {
		colorCategories[category] = '#8459A4';
	} else {
		colorCategories[category] = strongColorPattern[(i+offset)%l];
	}

});

var violationGrade3Color = '#F07B38';
var violationGrade2Color = '#FFDA60';
var violationGrade1Color = '#52bd34';

var violationTypeGrades;
SearchFormUtil.getViolationGrades(function(violations) {
	violationTypeGrades = violations;
});

var violationTypeColorCategories = {};
Object.keys(violationTypeGrades).map(function(type) {
	var color;
	switch(violationTypeGrades[type]) {
		case 1:
			color = violationGrade1Color;
		break;
		case 2:
			color = violationGrade2Color;
		break;
		case 3:
			color = violationGrade3Color;
		break;
	}
	violationTypeColorCategories[type] = color;
});
violationTypeColorCategories['_categories'] = violationTypeGrades;

// *** Initial data for this Vue component *** //

var initialData = {
	// UI
	_styles: styles,
	loadingData: true,
	emptyAggregations: false,
	error: false,
	activity: 0,
	pendingScroll: false,
	// Data synced with SearchForm component
	formModel: { },
	fields: [],
	// Data which will be sent to searchResult
	formData: {
		formModel: {},
		fields: [],
	},
	// Function which returns a data-set
	grade3ViolationChart: {
		getContent: null, // We use a function to give data to the chart to avoid "indexing" by Vue
	},
	orgViolations: {
		getContent: null,
	},
	violationDistributionChart: {
		getContent: null,
	},
	violationTypeDistributionChart: {
		getContent: null
	},
	// Misc
	orgs: orgs,
	// Colors
	colorCategories: colorCategories,
	colorPattern: colorPattern,
	violationTypeColorCategories: violationTypeColorCategories,
	violationGrade3Color: violationGrade3Color,
	violationGrade2Color: violationGrade2Color,
	violationGrade1Color: violationGrade1Color,
	// Carousel
	_carouselConf: {
		items: 2,
		itemsDesktop : [1300,2],
	    itemsDesktopSmall : [1050,1]
	},
	visibleItems: [],
};

// *** Render! *** //

Vue.component('view', Inspector);

new Vue({
	el: '#app'
});
<commit_msg>wrong colors<commit_after>'use strict';

// Vendor
var Vue = require('vue');
var $ = require('jquery');
var _cloneDeep = require('lodash/lang/cloneDeep');
var _assign = require('lodash/object/assign');
// Components
var SiteWrapper = require('components/SiteWrapper/SiteWrapper.js');
var Chart = require('components/Chart/Chart.js');
var SearchForm = require('components/SearchForm/SearchForm.js');
var SearchResult = require('components/SearchResult/SearchResult.js');
var AmbiguitiesTool = require('components/AmbiguitiesTool/AmbiguitiesTool.js');
var DuplicatesTool = require('components/DuplicatesTool/DuplicatesTool.js');
var DuplicatesHelp = require('components/Helps/DuplicatesHelp/DuplicatesHelp.js');
var Carousel = require('components/Carousel/Carousel.js');
var ViolationsDropdown = require('components/ViolationsDropdown/ViolationsDropdown.js');
var AboutVialations = require('components/AboutVialations/AboutVialations.js');
var AboutDuplicates = require('components/AboutDuplicates/AboutDuplicates.js');
// Mxins
var FieldLabelMixin = require('mixins/FieldLabelMixin/FieldLabelMixin.js');
// Utils
var DataUtil = require('utils/DataUtil/DataUtil.js');
var SearchFormUtil = require('utils/SearchFormUtil/SearchFormUtil.js');
var FormatAggregationUtil = require('utils/FormatAggregationUtil/FormatAggregationUtil.js');
var getQueryVariable = require('utils/getQueryVariable.js');
// CSS-modules
var styles = _assign(require('!!style!css?modules!./inspector.css'), require('css/modules/Colors.less'));
// CSS
require('css/theme-inspector.less');

/**
 * Inspector-view
 */

var Inspector = {
	_t: null, // Timeout reference
	mixins: [FieldLabelMixin],
	template: require('./inspector.html'),
	data: function() {
		return initialData;
	},
	watch: {
		/**
		 * On formModel-change, get aggregations from server
		 */
		'formModel': function(value) {
			clearTimeout(this._t);
			console.log('fm:' + JSON.stringify(value))
			this._t = setTimeout(function() {

				DataUtil.getFilterAggregations(this.formModel, function(aggregations) {
					if(!aggregations.error) {
						SearchFormUtil.handleAggregations(aggregations);
						this.setAggregations(aggregations);
						this.$set('error', false);
					} else {
						this.$set('error', true);
					}
					this.$set('loadingData', false);
				}.bind(this));
			}.bind(this), 800);
		},
		'formData': function(value) {
			/*console.log('fd:'+JSON.stringify(value))*/
		},
		'activity': function(value) {
			/*console.log('activity:'+JSON.stringify(value))*/
		}

	},
	components: {
		'site-wrapper': SiteWrapper,
		'chart': Chart,
		'search-form': SearchForm,
		'search-result': SearchResult,
		'duplicates-help': DuplicatesHelp,
		'ambiguities-tool': AmbiguitiesTool,
		'duplicates-tool': DuplicatesTool,
		'carousel': Carousel,
		'violations-dropdown': ViolationsDropdown,
		'about-vialations': AboutVialations,
		'about-duplicates': AboutDuplicates
	},
	ready: function() {

		/**
		 * Broadcast events based on url-parameters
		 */
		Vue.nextTick(function() {
			// &org=
			var org = getQueryVariable('org');
			if(org) { this.$broadcast('set-org-value', org); }
			// &from= and &to=
			var from = getQueryVariable('from');
			var to = getQueryVariable('to');
			var time = {};
			if(from) {
				time.from = from;
			}
			if(to) {
				time.to = to;
			}
			this.$broadcast('set-time-values', time);
			// &activity=
			var activity = getQueryVariable('activity');
            if(activity && (org || to || time)) {
                // Wait for next tick as some form-elements are being set
                require('vue').nextTick(function() {
                    this.$emit('start-activity', activity);
                }.bind(this));
            } else if(activity) {
                this.$emit('start-activity', activity);
            }
		}.bind(this));
	},
	events: {
        /**
         * Start an activity
         */
        'start-activity': function(activity) {
        	switch(activity) {
        		case 'VIOLATIONS':
        			this.startActivity(activity);
        		case 'AMBIGUITIES':
        			this.startActivity(activity);
        		break;
        	}
		}
	},
	methods: {
		/**
		 * On submission of FormModel
		 * @param {Object} formData
		 */
		onChange: function(formData) {
			this.$set('fields', formData.fields);
			formData.formModel.aggregate = 'inspector';
			this.$set('formModel', formData.formModel);
		},
		/**
		 * Called when SearchResult has received a result. Scroll down to component if pendingScroll flag is true
		 */
		onResultReceived: function() {
			if(this.pendingScroll === true) {
				$('html, body').animate({
					scrollTop: $(this.$els.searchResult).offset().top,
				}, 900);
				this.$set('pendingScroll', false);
			}
		},
		/**
		 *
		 */
		onCarouselNavigate: function(status) {
			this.$set('visibleItems', status.visibleItems);
		},
		/**
		 * Callback sent to Chart for selecting a specific org within the Form
		 * @prop {Object} e the org
		 */
		onClickOrg: function(e) {
			if(e.id && this.formModel.org !== e.id) {
				this.$broadcast('set-org-value', e.id);
			} else {
				this.$broadcast('set-org-value', '');
			}
		},
		/**
		 * Callback sent to Chart for selecting a specific violation
		 * @prop {Object} e the violation
		 */
		onClickViolation: function(e) {
			$('html, body').animate({
				scrollTop: $(this.$els.searchForm).offset().top-25,
			}, 900);
			SearchFormUtil.getViolations(function(violations) {
				Object.keys(violations).map(function(v) {
					if(violations[v].text === e.id) {
						this.onClickViolationOption(v, violations[v]);
					}
				}.bind(this))
			}.bind(this));
		},
		/**
		 * Callback sent to ViolationDropdown
		 * @param {String} code
		 * @param {Object} violation
		 */
		onClickViolationOption: function(code, violation) {
			// Clear
			this.$set('formModel.violation', undefined);
			this.$set('fields', (this.fields || []).filter(function(field) {
				return field && field.fieldName && field.fieldName !== 'violation';
			}));
			//For sending selected violation to list
			/*
			if (violation.text !== undefined ) {
				ViolationsDropdown.methods.compareActive(violation.text);
			}
			*/
			// Add to formModel
			if(typeof code === 'string') {
				this.$set('formModel.violation', code);
				this.fields.push({
					fieldName: 'violation',
					value: code,
					labels: [{
						text: violation.text
					}]
				});
			}

			// Start error-activity

			this.$emit('start-activity', 'VIOLATIONS');
		},
		onClickViolationButton: function(violation) {
			// Clear
			//this.$set('formModel.violation', undefined);
			// Start error-activity
			$(".chooseViolationSection").show();
			this.$emit('start-activity', 'VIOLATIONS');

		},
		/**
		 * Gives site access to recieve the the state the user left
		 */
		onClickExternal: function() {
			localStorage.setItem('externalPass', true);
		},
		/**
		 * Starts an activity
		 * @param {String} activity
		 */
		startActivity: function(activity) {

			if (activity === 'LOCAL_DUPLICATES' || activity === 'AMBIGUITIES') {
				$(".chooseViolationSection").hide();
			}

			var formData = {
				formModel: _cloneDeep(this.formModel),
				fields: _cloneDeep(this.fields)
			}
			switch(activity) {
				case 'VIOLATIONS':
					formData.formModel.templateName = 'quality';
				break;
				case 'LOCAL_DUPLICATES':
					formData.formModel.templateName = 'duplicates';
				break;
				case 'AMBIGUITIES':
					formData.formModel.templateName = 'AmbiguityListing';
				break;
			}
			this.$set('pendingScroll', true);
			this.$set('formData', formData);
			this.$set('activity', activity);
		},
		/**
		 * Format aggregations and pass them on to the respective charts
		 * @param {Object} aggregations
		 */
		setAggregations: function(aggregations) {
			// *** LINE CHART *** //
			this.setChartContent('grade3ViolationChart', FormatAggregationUtil.toGrade3ViolationRatioYearTimeSeries(aggregations, this.formModel.org.length === 0));
			// *** BAR CHART *** //
			this.setChartContent('orgViolations', FormatAggregationUtil.toOrgViolationRatio(aggregations, this.formModel.org.length === 0 ? 5 : 10000));
			// *** VIOLATION TYPE DISTRIBUTION CHART *** //
			var aggs = FormatAggregationUtil.toViolationTypeDistributions(aggregations, this.formModel.org);
			aggs.columns.forEach(function(column) {
				SearchFormUtil.getViolations(function(violations) {
					Object.keys(violations).forEach(function(v) {
						if(violations[v].name === column[0]) {
							column[0] = violations[v].text;
						}
					});
				});
			});
			this.setChartContent('violationTypeDistributionChart', aggs);
			// *** PIE CHART *** //
			this.setChartContent('violationDistributionChart', FormatAggregationUtil.toViolationDistribution(aggregations));
		},
		/**
		 * Sets .getContent function for a chart-object
		 * @param {String} chartPath
		 * @param {Object} aggregations
		 */
		setChartContent: function(chartPath, aggregations) {
			if(aggregations.columns && aggregations.columns.length > 0) {
				this.$set(chartPath + '.getContent', function() {
					return aggregations;
				});
			}
			else {
				this.$set(chartPath + '.getContent', null);
			}
		}

	}
};

/**
 * Should a slide be mounted in the carousel?
 * @ {Object} d
 * @ {String} index
 * @ {Array} visibleItems
 */
Vue.filter('visible', function(d, index, visibleItems) {
	index = Number(index)-1;
	var visible = visibleItems.filter(function(item) {
		return item === index;
	});
	if(visible.length === 1) {
		return d;
	}
});

// *** Define colors and categories for charts! *** //

var colorPattern = ['#FFC300','#FFCB20','#FFD240','#FFDA60','#FFE180','#FFE99F','#FFF0BF','#FFF8DF','#EE681B','#F07B38','#F28E54','#F4A171','#F6B38D','#F9C6AA','#FBD9C6','#FDECE3','#9E0634','#AA254D','#B64467','#C26380','#CF839A','#DBA2B3','#E7C1CC','#F3E0E6','#5B2285','#703E94','#8459A4','#9875B3','#AD91C2','#C1ACD1','#D6C8E1','#EAE3F0','#61B5BF','#75BEC7','#89C8CF','#9CD1D7','#B0DADF','#C4E3E7','#D7ECEF','#EBF6F7'];
var strongColorPattern = ['#FFC300', '#FFCB20', '#FFD240', '#FFDA60', '#EE681B', '#F07B38', '#F28E54', '#F4A171', '#9E0634', '#AA254D', '#B64467', '#C26380', '#5B2285', '#703E94', '#8459A4', '#9875B3', '#61B5BF', '#75BEC7', '#89C8CF', '#9CD1D7'];

var orgs = ['bth','cth','du','esh','fhs','gih','gu','hb','hh','hhs','hig','his','hj','hkr','hv','kau','ki','kmh','konstfack','kth','liu','lnu','ltu','lu','mah','mdh','miun','nai','nationalmuseum','naturvardsverket','nrm','oru','rkh','sh','shh','slu','su','umu','uu','vti'];
var categories = orgs.concat(['Övriga','Alla lärosäten','Bristfälliga poster','Felfria poster']);

var colorCategories = {};

var l = strongColorPattern.length;
var offset = Math.floor(strongColorPattern.length/2);
offset = 7;
categories.forEach(function(category, i) {
	console.log(category)
	if(category === 'Felfria poster') {
		colorCategories[category] = '#FFDA60';
	} else if(category === 'Bristfälliga poster') {
		colorCategories[category] = '#FFC300';
	} else if(category === 'Alla lärosäten') {
		colorCategories[category] = '#8459A4';
	} else {
		colorCategories[category] = strongColorPattern[(i+offset)%l];
	}

});

var violationGrade3Color = '#F07B38';
var violationGrade2Color = '#FFDA60';
var violationGrade1Color = '#52bd34';

var violationTypeGrades;
SearchFormUtil.getViolationGrades(function(violations) {
	violationTypeGrades = violations;
});

var violationTypeColorCategories = {};
Object.keys(violationTypeGrades).map(function(type) {
	var color;
	switch(violationTypeGrades[type]) {
		case 1:
			color = violationGrade1Color;
		break;
		case 2:
			color = violationGrade2Color;
		break;
		case 3:
			color = violationGrade3Color;
		break;
	}
	violationTypeColorCategories[type] = color;
});
violationTypeColorCategories['_categories'] = violationTypeGrades;

// *** Initial data for this Vue component *** //

var initialData = {
	// UI
	_styles: styles,
	loadingData: true,
	emptyAggregations: false,
	error: false,
	activity: 0,
	pendingScroll: false,
	// Data synced with SearchForm component
	formModel: { },
	fields: [],
	// Data which will be sent to searchResult
	formData: {
		formModel: {},
		fields: [],
	},
	// Function which returns a data-set
	grade3ViolationChart: {
		getContent: null, // We use a function to give data to the chart to avoid "indexing" by Vue
	},
	orgViolations: {
		getContent: null,
	},
	violationDistributionChart: {
		getContent: null,
	},
	violationTypeDistributionChart: {
		getContent: null
	},
	// Misc
	orgs: orgs,
	// Colors
	colorCategories: colorCategories,
	colorPattern: colorPattern,
	violationTypeColorCategories: violationTypeColorCategories,
	violationGrade3Color: violationGrade3Color,
	violationGrade2Color: violationGrade2Color,
	violationGrade1Color: violationGrade1Color,
	// Carousel
	_carouselConf: {
		items: 2,
		itemsDesktop : [1300,2],
	    itemsDesktopSmall : [1050,1]
	},
	visibleItems: [],
};

// *** Render! *** //

Vue.component('view', Inspector);

new Vue({
	el: '#app'
});
<|endoftext|>


<commit_before>const fs = require('fs')
const path = require('path')

const interfaceManifest = {
  LOG: {
    name: 'log',
    input: ['readOffset', 'length', 'i32', 'ipointer', 'ipointer', 'ipointer', 'ipointer'],
    output: []
  },
  CALLDATALOAD: {
    name: 'callDataCopy256',
    input: ['pointer'],
    output: ['i256'] // TODO: this is wrong
  },
  GAS: {
    name: 'getGasLeft',
    input: [],
    output: ['i64']
  },
  ADDRESS: {
    name: 'getAddress',
    input: [],
    output: ['address']
  },
  BALANCE: {
    name: 'getBalance',
    async: true,
    input: ['address'],
    output: ['i128']
  },
  ORIGIN: {
    name: 'getTxOrigin',
    input: [],
    output: ['address']
  },
  CALLER: {
    name: 'getCaller',
    input: [],
    output: ['address']
  },
  CALLVALUE: {
    name: 'getCallValue',
    input: [],
    output: ['i128']
  },
  CALLDATASIZE: {
    name: 'getCallDataSize',
    input: [],
    output: ['i32']
  },
  CALLDATACOPY: {
    name: 'callDataCopy',
    input: ['writeOffset', 'i32', 'length'],
    output: []
  },
  CODESIZE: {
    name: 'getCodeSize',
    async: true,
    input: [],
    output: ['i32']
  },
  CODECOPY: {
    name: 'codeCopy',
    async: true,
    input: ['writeOffset', 'i32', 'length'],
    output: []
  },
  EXTCODESIZE: {
    name: 'getExternalCodeSize',
    async: true,
    input: ['address'],
    output: ['i32']
  },
  EXTCODECOPY: {
    name: 'externalCodeCopy',
    async: true,
    input: ['address', 'writeOffset', 'i32', 'length'],
    output: []
  },
  GASPRICE: {
    name: 'getTxGasPrice',
    input: ['opointer'],
    output: []
  },
  BLOCKHASH: {
    name: 'getBlockHash',
    async: true,
    input: ['i32'],
    output: ['i256']
  },
  COINBASE: {
    name: 'getBlockCoinbase',
    input: [],
    output: ['address']
  },
  TIMESTAMP: {
    name: 'getBlockTimestamp',
    input: [],
    output: ['i64']
  },
  NUMBER: {
    name: 'getBlockNumber',
    input: [],
    output: ['i64']
  },
  DIFFICULTY: {
    name: 'getBlockDifficulty',
    input: ['opointer'],
    output: []
  },
  GASLIMIT: {
    name: 'getBlockGasLimit',
    input: [],
    output: ['i64']
  },
  CREATE: {
    name: 'create',
    async: true,
    input: ['i128', 'readOffset', 'length'],
    output: ['address']
  },
  CALL: {
    name: 'call',
    async: true,
    input: ['i64', 'address', 'i128', 'readOffset', 'length', 'writeOffset', 'length'],
    output: ['i32']
  },
  CALLCODE: {
    name: 'callCode',
    async: true,
    input: ['i32', 'address', 'i128', 'readOffset', 'length', 'writeOffset', 'length'],
    output: ['i32']
  },
  DELEGATECALL: {
    name: 'callDelegate',
    async: true,
    input: ['i32', 'address', 'i128', 'readOffset', 'length', 'writeOffset', 'length'],
    output: ['i32']
  },
  SSTORE: {
    name: 'storageStore',
    async: true,
    input: ['ipointer', 'ipointer'],
    output: []
  },
  SLOAD: {
    name: 'storageLoad',
    async: true,
    input: ['ipointer'],
    output: ['i256'] // TODO: this is wrong
  },
  SELFDESTRUCT: {
    name: 'selfDestruct',
    input: ['address'],
    output: []
  },
  RETURN: {
    name: 'return',
    input: ['readOffset', 'length'],
    output: []
  }
}

function generateManifest (interfaceManifest, opts) {
  const useAsyncAPI = opts.useAsyncAPI
  const json = {}
  for (let opcode in interfaceManifest) {
    const op = interfaceManifest[opcode]
      // generate the import params
    let inputs = op.input.map(input => input === 'i64' ? 'i64' : 'i32').concat(op.output.filter(type => type !== 'i32' && type !== 'i64').map(() => 'i32'))
    let params = ''

    if (useAsyncAPI && op.async) {
      inputs.push('i32')
    }

    if (inputs.length) {
      params = `(param ${inputs.join(' ')})`
    }

    let result = ''
    const firstResult = op.output[0]
    if (firstResult === 'i32' || firstResult === 'i64') {
      result = `(result ${firstResult})`
    }
    // generate import
    const imports = `(import "ethereum" "${op.name}" (func $${op.name} ${params} ${result}))`
    let wasm = ';; generated by ./wasm/generateInterface.js\n'
      // generate function
    wasm += `(func $${opcode} `
    if (useAsyncAPI && op.async) {
      wasm += '(param $callback i32)'
    }

    let locals = ''
    let body = ''

    // generate the call to the interface
    let spOffset = 0
    let numOfLocals = 0
    let lastOffset
    let call = `(call $${op.name}`
    op.input.forEach((input) => {
      // TODO: remove 'pointer' type, replace with 'ipointer' or 'opointer'
      if (input === 'i128' || input == 'address' || input == 'pointer') {
        if (spOffset) {
          call += `(i32.add (get_global $sp) (i32.const ${spOffset * 32}))`
        } else {
          call += '(get_global $sp)'
        }
      } else if (input === 'ipointer') {
        // input pointer
        // points to a wasm memory offset where input data will be read
        // the wasm memory offset is an existing item on the EVM stack
        if (spOffset) {
          call += `(i32.add (get_global $sp) (i32.const ${spOffset * 32}))`
        } else {
          call += '(get_global $sp)'
        }
      } else if (input === 'opointer') {
        // output pointer
        // points to a wasm memory offset where the result should be written
        // the wasm memory offset is a new item on the EVM stack
        spOffset++
        call += `(i32.add (get_global $sp) (i32.const ${spOffset * 32}))`
      } else if (input === 'i32') {
        call += `(call $check_overflow
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3}))))`
      } else if (input === 'i64') {
        call += `(call $check_overflow_i64
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3}))))`
      } else if (input === 'writeOffset' || input === 'readOffset') {
        lastOffset = input
        locals += `(local $offset${numOfLocals} i32)`
        body += `(set_local $offset${numOfLocals}
    (call $check_overflow
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})))))`
        call += `(get_local $offset${numOfLocals})`
      } else if (input === 'length') {
        locals += `(local $length${numOfLocals} i32)`
        body += `(set_local $length${numOfLocals}
    (call $check_overflow
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})))))

    (call $memusegas (get_local $offset${numOfLocals}) (get_local $length${numOfLocals}))
    (set_local $offset${numOfLocals} (i32.add (get_global $memstart) (get_local $offset${numOfLocals})))`

        call += `(get_local $length${numOfLocals})`
        numOfLocals++
      }
      spOffset--
    })

    spOffset++

    // generate output handling
    const output = op.output.shift()
    if (output === 'i128') {
      call =
        `${call} (i32.add (get_global $sp) (i32.const ${spOffset * 32}))`

      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }

      call += `)
    ;; zero out mem
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})) (i64.const 0))`

      if (!op.async) {
        call += '(drop (call $bswap_m128 (i32.add (i32.const 32)(get_global $sp))))'
      }
    } else if (output === 'address') {
      call =
        `${call} (i32.add (get_global $sp) (i32.const ${spOffset * 32}))`

      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }

      call += `)
    (drop (call $bswap_m160 (i32.add (get_global $sp) (i32.const ${spOffset * 32}))))
    ;; zero out mem
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})) (i64.const 0))
    (i32.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2 + 4})) (i32.const 0))`
    } else if (output === 'i256') {
      call = `${call}
    (i32.add (get_global $sp)
    (i32.const ${spOffset * 32}))`

      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }

      call += `)
      (drop (call $bswap_m256 (i32.add (i32.const 32) (get_global $sp))))
      `
    } else if (output === 'i32') {
      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }

      call =
        `(i64.store
    (i32.add (get_global $sp) (i32.const ${spOffset * 32}))
    (i64.extend_u/i32
      ${call})))

    ;; zero out mem
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})) (i64.const 0))`
    } else if (output === 'i64') {
      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }
      call =
        `(i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32})) ${call}))

    ;; zero out mem
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})) (i64.const 0))`
    } else if (!output) {
      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }
      call += ')'
    }

    wasm += `${locals} ${body} ${call})`
    json[opcode] = {
      wast: wasm,
      imports: imports
    }
  }
  return json
}

// generateManifest mutates the input, so use a copy
const interfaceManifestCopy = JSON.parse(JSON.stringify(interfaceManifest))

let syncJson = generateManifest(interfaceManifest, {'useAsyncAPI': false})
let asyncInterfaceJson = generateManifest(interfaceManifestCopy, {'useAsyncAPI': true})
// add math ops
const files = fs.readdirSync(__dirname).filter(file => file.slice(-5) === '.wast')
files.forEach((file) => {
  const wast = fs.readFileSync(path.join(__dirname, file)).toString()
  file = file.slice(0, -5)
  // don't overwrite import generation
  syncJson[file] = syncJson[file] || {}
  syncJson[file].wast = wast

  asyncInterfaceJson[file] = asyncInterfaceJson[file] || {}
  asyncInterfaceJson[file].wast = wast
})

fs.writeFileSync(path.join(__dirname, 'wast.json'), JSON.stringify(syncJson, null, 2))
fs.writeFileSync(path.join(__dirname, 'wast-async.json'), JSON.stringify(asyncInterfaceJson, null, 2))
<commit_msg>Reorder generateInterface.js for easier upgradeability for C++<commit_after>const fs = require('fs')
const path = require('path')

const interfaceManifest = {
  LOG: {
    name: 'log',
    input: ['readOffset', 'length', 'i32', 'ipointer', 'ipointer', 'ipointer', 'ipointer'],
    output: []
  },
  CALLDATALOAD: {
    name: 'callDataCopy256',
    input: ['pointer'],
    output: ['i256'] // TODO: this is wrong
  },
  GAS: {
    name: 'getGasLeft',
    input: [],
    output: ['i64']
  },
  ADDRESS: {
    name: 'getAddress',
    input: [],
    output: ['address']
  },
  BALANCE: {
    name: 'getBalance',
    async: true,
    input: ['address'],
    output: ['i128']
  },
  ORIGIN: {
    name: 'getTxOrigin',
    input: [],
    output: ['address']
  },
  CALLER: {
    name: 'getCaller',
    input: [],
    output: ['address']
  },
  CALLVALUE: {
    name: 'getCallValue',
    input: [],
    output: ['i128']
  },
  CALLDATASIZE: {
    name: 'getCallDataSize',
    input: [],
    output: ['i32']
  },
  CALLDATACOPY: {
    name: 'callDataCopy',
    input: ['writeOffset', 'i32', 'length'],
    output: []
  },
  CODESIZE: {
    name: 'getCodeSize',
    async: true,
    input: [],
    output: ['i32']
  },
  CODECOPY: {
    name: 'codeCopy',
    async: true,
    input: ['writeOffset', 'i32', 'length'],
    output: []
  },
  EXTCODESIZE: {
    name: 'getExternalCodeSize',
    async: true,
    input: ['address'],
    output: ['i32']
  },
  EXTCODECOPY: {
    name: 'externalCodeCopy',
    async: true,
    input: ['address', 'writeOffset', 'i32', 'length'],
    output: []
  },
  GASPRICE: {
    name: 'getTxGasPrice',
    input: ['opointer'],
    output: []
  },
  BLOCKHASH: {
    name: 'getBlockHash',
    async: true,
    input: ['i32'],
    output: ['i256']
  },
  COINBASE: {
    name: 'getBlockCoinbase',
    input: [],
    output: ['address']
  },
  TIMESTAMP: {
    name: 'getBlockTimestamp',
    input: [],
    output: ['i64']
  },
  NUMBER: {
    name: 'getBlockNumber',
    input: [],
    output: ['i64']
  },
  DIFFICULTY: {
    name: 'getBlockDifficulty',
    input: ['opointer'],
    output: []
  },
  GASLIMIT: {
    name: 'getBlockGasLimit',
    input: [],
    output: ['i64']
  },
  CREATE: {
    name: 'create',
    async: true,
    input: ['i128', 'readOffset', 'length'],
    output: ['address']
  },
  CALL: {
    name: 'call',
    async: true,
    input: ['i64', 'address', 'i128', 'readOffset', 'length', 'writeOffset', 'length'],
    output: ['i32']
  },
  CALLCODE: {
    name: 'callCode',
    async: true,
    input: ['i32', 'address', 'i128', 'readOffset', 'length', 'writeOffset', 'length'],
    output: ['i32']
  },
  DELEGATECALL: {
    name: 'callDelegate',
    async: true,
    input: ['i32', 'address', 'i128', 'readOffset', 'length', 'writeOffset', 'length'],
    output: ['i32']
  },
  SSTORE: {
    name: 'storageStore',
    async: true,
    input: ['ipointer', 'ipointer'],
    output: []
  },
  SLOAD: {
    name: 'storageLoad',
    async: true,
    input: ['ipointer'],
    output: ['i256'] // TODO: this is wrong
  },
  SELFDESTRUCT: {
    name: 'selfDestruct',
    input: ['address'],
    output: []
  },
  RETURN: {
    name: 'return',
    input: ['readOffset', 'length'],
    output: []
  }
}

function generateManifest (interfaceManifest, opts) {
  const useAsyncAPI = opts.useAsyncAPI
  const json = {}
  for (let opcode in interfaceManifest) {
    const op = interfaceManifest[opcode]
      // generate the import params
    let inputs = op.input.map(input => input === 'i64' ? 'i64' : 'i32').concat(op.output.filter(type => type !== 'i32' && type !== 'i64').map(() => 'i32'))
    let params = ''

    if (useAsyncAPI && op.async) {
      inputs.push('i32')
    }

    if (inputs.length) {
      params = `(param ${inputs.join(' ')})`
    }

    let result = ''
    const firstResult = op.output[0]
    if (firstResult === 'i32' || firstResult === 'i64') {
      result = `(result ${firstResult})`
    }
    // generate import
    const imports = `(import "ethereum" "${op.name}" (func $${op.name} ${params} ${result}))`
    let wasm = ';; generated by ./wasm/generateInterface.js\n'
      // generate function
    wasm += `(func $${opcode} `
    if (useAsyncAPI && op.async) {
      wasm += '(param $callback i32)'
    }

    let locals = ''
    let body = ''

    // generate the call to the interface
    let spOffset = 0
    let numOfLocals = 0
    let lastOffset
    let call = `(call $${op.name}`
    op.input.forEach((input) => {
      // TODO: remove 'pointer' type, replace with 'ipointer' or 'opointer'
      if (input === 'i128' || input == 'address' || input == 'pointer') {
        if (spOffset) {
          call += `(i32.add (get_global $sp) (i32.const ${spOffset * 32}))`
        } else {
          call += '(get_global $sp)'
        }
      } else if (input === 'ipointer') {
        // input pointer
        // points to a wasm memory offset where input data will be read
        // the wasm memory offset is an existing item on the EVM stack
        if (spOffset) {
          call += `(i32.add (get_global $sp) (i32.const ${spOffset * 32}))`
        } else {
          call += '(get_global $sp)'
        }
      } else if (input === 'opointer') {
        // output pointer
        // points to a wasm memory offset where the result should be written
        // the wasm memory offset is a new item on the EVM stack
        spOffset++
        call += `(i32.add (get_global $sp) (i32.const ${spOffset * 32}))`
      } else if (input === 'i32') {
        call += `(call $check_overflow
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3}))))`
      } else if (input === 'i64') {
        call += `(call $check_overflow_i64
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})))
           (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3}))))`
      } else if (input === 'writeOffset' || input === 'readOffset') {
        lastOffset = input
        locals += `(local $offset${numOfLocals} i32)`
        body += `(set_local $offset${numOfLocals}
    (call $check_overflow
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})))))`
        call += `(get_local $offset${numOfLocals})`
      } else if (input === 'length') {
        locals += `(local $length${numOfLocals} i32)`
        body += `(set_local $length${numOfLocals}
    (call $check_overflow
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})))
      (i64.load (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})))))

    (call $memusegas (get_local $offset${numOfLocals}) (get_local $length${numOfLocals}))
    (set_local $offset${numOfLocals} (i32.add (get_global $memstart) (get_local $offset${numOfLocals})))`

        call += `(get_local $length${numOfLocals})`
        numOfLocals++
      }
      spOffset--
    })

    spOffset++

    // generate output handling
    const output = op.output.shift()
    if (output === 'i128') {
      call =
        `${call} (i32.add (get_global $sp) (i32.const ${spOffset * 32}))`

      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }

      call += `)
    ;; zero out mem
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})) (i64.const 0))`

      if (!op.async) {
        call += '(drop (call $bswap_m128 (i32.add (i32.const 32)(get_global $sp))))'
      }
    } else if (output === 'address') {
      call =
        `${call} (i32.add (get_global $sp) (i32.const ${spOffset * 32}))`

      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }

      call += `)
    (drop (call $bswap_m160 (i32.add (get_global $sp) (i32.const ${spOffset * 32}))))
    ;; zero out mem
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})) (i64.const 0))
    (i32.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2 + 4})) (i32.const 0))`
    } else if (output === 'i256') {
      call = `${call}
    (i32.add (get_global $sp)
    (i32.const ${spOffset * 32}))`

      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }

      call += `)
      (drop (call $bswap_m256 (i32.add (i32.const 32) (get_global $sp))))
      `
    } else if (output === 'i32') {
      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }

      call =
        `(i64.store
    (i32.add (get_global $sp) (i32.const ${spOffset * 32}))
    (i64.extend_u/i32
      ${call})))

    ;; zero out mem
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})) (i64.const 0))`
    } else if (output === 'i64') {
      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }
      call =
        `(i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32})) ${call}))

    ;; zero out mem
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 3})) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8 * 2})) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const ${spOffset * 32 + 8})) (i64.const 0))`
    } else if (!output) {
      if (useAsyncAPI && op.async) {
        call += '(get_local $callback)'
      }
      call += ')'
    }

    wasm += `${locals} ${body} ${call})`
    json[opcode] = {
      wast: wasm,
      imports: imports
    }
  }

  // add math ops
  const files = fs.readdirSync(__dirname).filter(file => file.slice(-5) === '.wast')
  files.forEach((file) => {
    const wast = fs.readFileSync(path.join(__dirname, file)).toString()
    file = file.slice(0, -5)
    // don't overwrite import generation
    json[file] = json[file] || {}
    json[file].wast = wast
  })

  return json
}

// generateManifest mutates the input, so use a copy
const interfaceManifestCopy = JSON.parse(JSON.stringify(interfaceManifest))

let syncJson = generateManifest(interfaceManifest, {'useAsyncAPI': false})
let asyncInterfaceJson = generateManifest(interfaceManifestCopy, {'useAsyncAPI': true})

fs.writeFileSync(path.join(__dirname, 'wast.json'), JSON.stringify(syncJson, null, 2))
fs.writeFileSync(path.join(__dirname, 'wast-async.json'), JSON.stringify(asyncInterfaceJson, null, 2))
<|endoftext|>