<commit_before>/**
 *
 */
/*global define, brackets, describe, beforeEach, afterEach, it, expect, runs, waitsForDone*/
define(function (require, exports, module) {
    "use strict";
    var SpecRunnerUtils = brackets.getModule("spec/SpecRunnerUtils"),
        FileUtils = brackets.getModule("file/FileUtils");
    describe("Code Folding", function () {
        var testWindow,
            testEditor,
            EditorManager,
            DocumentManager,
            CommandManager,
            PreferencesManager,
            prefs,
            cm,
            gutterName = "CodeMirror-foldgutter",
            foldMarkerOpen = gutterName + "-open",
            foldMarkerClosed = gutterName + "-close";
        var extensionPath = FileUtils.getNativeModuleDirectoryPath(module),
            testDocPath = extensionPath + "/unittest-files/",
            testFilePath = testDocPath + "test.js";

        /**
         * Utility to temporarily set preference values in the session scope
         */
        function setPreference(key, value) {
            prefs.set(key, value, {
                locations: {
                    scope: "session"
                }
            });
        }

        function openTestFile(path) {
            var promise = SpecRunnerUtils.openProjectFiles([path]);
            promise.then(function () {
                testEditor = EditorManager.getCurrentFullEditor();
                cm = testEditor._codeMirror;
            });
            waitsForDone(promise, "Test file opened", 3000);
        }

        function setup() {
            runs(function () {
                SpecRunnerUtils.createTestWindowAndRun(this, function (w) {
                    testWindow = w;
                    EditorManager = w.brackets.test.EditorManager;
                    DocumentManager = w.brackets.test.DocumentManager;
                    PreferencesManager = w.brackets.test.PreferencesManager;
                    CommandManager = w.brackets.test.CommandManager;

                    prefs = PreferencesManager.getExtensionPrefs("code-folding");
                }, {hasNativeMenus: true});
            });

            runs(function () {
                setPreference("saveFoldStates", false);
                SpecRunnerUtils.loadProjectInTestWindow(testDocPath);
            });

            runs(function () {
                openTestFile(testFilePath);
            });

            runs(function () {
                testEditor = EditorManager.getActiveEditor();
                cm = testEditor._codeMirror;
            });

        }

        function tearDown() {
            SpecRunnerUtils.closeTestWindow();
        }

        function runCommand(command) {
            return CommandManager.execute(command);
        }

        function foldCodeOnLine(line) {
            cm.setCursor(line);
            var promise = runCommand("codefolding.collapse");
            waitsForDone(promise, "Collapse code", 2000);
        }

        function expandCodeOnLine(line) {
            cm.setCursor(line);
            var promise = runCommand("codefolding.expand");
            waitsForDone(promise, "Expand code", 2000);
        }

        function getEditorFoldMarks() {
            var marks = cm.getAllMarks().filter(function (m) {
                return m.__isFold;
            });
            return marks;
        }

        function gutterMarkState(lineInfo) {
            var classes = lineInfo.gutterMarkers[gutterName].classList;
            if (classes && classes.contains(foldMarkerClosed)) {
                return "close";
            } else if (classes && classes.contains(foldMarkerOpen)) {
                return "open";
            }
            return;
        }

        function getGutterFoldMarks() {
            var marks = [];
            cm.eachLine(function (lineHandle) {
                var lineInfo = cm.lineInfo(lineHandle);
                marks.push(gutterMarkState(lineInfo));
            });

            return marks.filter(function (m) { return m; });
        }

        beforeEach(setup);
        afterEach(tearDown);

        it("gutter fold marks are rendered on startup", function () {
            var marks = getGutterFoldMarks();
            expect(marks.length).toBeGreaterThan(0);
        });

        it("Folding code creates a folded region in editor", function () {
            runs(function () {
                foldCodeOnLine(1);
            });

            runs(function () {
                var marks = getEditorFoldMarks();
                expect(marks.length).toEqual(1);
            });
        });

        it("Expanding code clears the folded region in editor", function () {
            runs(function () {
                foldCodeOnLine(1);
            });
            runs(function () {
                expandCodeOnLine(1);
            });

            runs(function () {
                var marks = getEditorFoldMarks();
                expect(marks.length).toEqual(0);
            });
        });

        describe("Preferences work as expected", function () {
            afterEach(function () {
                testEditor = null;
                cm = null;
            });
            it("Persistence of fold states works as expected", function () {
                setPreference("saveFoldStates", true);
                runs(function () {
                    foldCodeOnLine(1);
                });
                runs(function () {
                    testWindow.closeAllFiles();
                });

                runs(function () {
                    openTestFile(testFilePath);
                });

                runs(function () {
                    //expect line 1 to be folded
                    var marks = getEditorFoldMarks();
                    expect(marks.length).toEqual(1);
                });
            });

            it("Persistence of fold states can be disabled", function () {
                setPreference("saveFoldStates", false);
                runs(function () {
                    foldCodeOnLine(1);
                });
                runs(function () {
                    testWindow.closeAllFiles();
                });

                runs(function () {
                    openTestFile(testFilePath);
                });

                runs(function () {
                    //expect line 1 to be folded
                    var marks = getEditorFoldMarks();
                    expect(marks.length).toEqual(0);
                });
            });

            it("Minimum fold size is obeyed", function () {
                setPreference("minFoldSize", 20000);
                runs(function () {
                    testWindow.closeAllFiles();
                });

                runs(function () {
                    openTestFile(testFilePath);
                });

                runs(function () {
                    var marks = getGutterFoldMarks();
                    expect(marks.length).toEqual(0);
                });
            });
        });

    });
});
<commit_msg>added test cases including enabling and disabling extension, and checking that preference values work as expected<commit_after>/**
 *
 */
/*global define, brackets, describe, beforeEach, afterEach, it, expect, runs, waitsForDone*/
define(function (require, exports, module) {
    "use strict";
    var SpecRunnerUtils = brackets.getModule("spec/SpecRunnerUtils"),
        FileUtils = brackets.getModule("file/FileUtils");
    describe("Code Folding", function () {
        var testWindow,
            testEditor,
            EditorManager,
            DocumentManager,
            CommandManager,
            PreferencesManager,
            prefs,
            cm,
            gutterName = "CodeMirror-foldgutter",
            foldMarkerOpen = gutterName + "-open",
            foldMarkerClosed = gutterName + "-folded";
        var extensionPath = FileUtils.getNativeModuleDirectoryPath(module),
            testDocPath = extensionPath + "/unittest-files/",
            testFilePath = testDocPath + "test.js";

        var open = "open", folded = "folded";

        /**
         * Utility to temporarily set preference values in the session scope
         */
        function setPreference(key, value) {
            prefs.set(key, value, {
                locations: {
                    scope: "session"
                }
            });
        }

        function openTestFile(path) {
            var promise = SpecRunnerUtils.openProjectFiles([path]);
            promise.then(function () {
                testEditor = EditorManager.getCurrentFullEditor();
                cm = testEditor._codeMirror;
            });
            waitsForDone(promise, "Test file opened", 3000);
        }

        function setup() {
            runs(function () {
                SpecRunnerUtils.createTestWindowAndRun(this, function (w) {
                    testWindow = w;
                    EditorManager = w.brackets.test.EditorManager;
                    DocumentManager = w.brackets.test.DocumentManager;
                    PreferencesManager = w.brackets.test.PreferencesManager;
                    CommandManager = w.brackets.test.CommandManager;

                    prefs = PreferencesManager.getExtensionPrefs("code-folding");
                }, {hasNativeMenus: true});
            });

            runs(function () {
                //setPreference("saveFoldStates", false);
                SpecRunnerUtils.loadProjectInTestWindow(testDocPath);
            });
        }

        function tearDown() {
            SpecRunnerUtils.closeTestWindow();
        }

        function runCommand(command) {
            return CommandManager.execute(command);
        }

        function foldCodeOnLine(line) {
            cm.setCursor(line);
            var promise = runCommand("codefolding.collapse");
            waitsForDone(promise, "Collapse code", 2000);
        }

        function expandCodeOnLine(line) {
            cm.setCursor(line);
            var promise = runCommand("codefolding.expand");
            waitsForDone(promise, "Expand code", 2000);
        }

        function getEditorFoldMarks() {
            testEditor = EditorManager.getCurrentFullEditor();
            cm = testEditor._codeMirror;

            var marks = cm.getAllMarks().filter(function (m) {
                return m.__isFold;
            });
            return marks;
        }

        function gutterMarkState(lineInfo) {
            var classes = lineInfo.gutterMarkers[gutterName].classList;
            if (classes && classes.contains(foldMarkerClosed)) {
                return {line: lineInfo.line, type: folded};
            } else if (classes && classes.contains(foldMarkerOpen)) {
                return {line: lineInfo.line, type: open};
            }
            return;
        }
        /**
         * Helper function to return the fold markers on the current codeMirror instance
         *
         * @returns {[[Type]]} [[Description]]
         */
        function getGutterFoldMarks() {
            testEditor = EditorManager.getCurrentFullEditor();
            cm = testEditor._codeMirror;
            var marks = [];
            cm.eachLine(function (lineHandle) {
                var lineInfo = cm.lineInfo(lineHandle);
                marks.push(gutterMarkState(lineInfo));
            });

            return marks.filter(function (m) { return m; });
        }

        function filterOpen(m) {
            return m.type === open;
        }

        function filterFolded(m) {
            return m.type === folded;
        }

        function getLineNumbers(m) {
            return m.line;
        }

        function toZeroIndex(lines) {
            return lines.map(function (l) {
                return l - 1;
            });
        }

        beforeEach(function () {
            setup();
            runs(function () {
                openTestFile(testFilePath);
            });
            runs(function () {
                openTestFile(testFilePath);
            });

            runs(function () {
                testEditor = EditorManager.getCurrentFullEditor();
                cm = testEditor._codeMirror;
            });

        });

        afterEach(function () {
            testWindow.closeAllFiles();
            tearDown();
        });

        describe("", function () {
            it("gutter fold marks are rendered on startup", function () {
                var marks = getGutterFoldMarks();
                expect(marks.length).toBeGreaterThan(0);
            });

            it("Folding code creates a folded region in editor", function () {
                var lineNumber = 1;
                runs(function () {
                    foldCodeOnLine(lineNumber);
                });

                runs(function () {
                    var marks = getEditorFoldMarks();
                    expect(marks.length).toEqual(1);
                    expect(marks[0].lines[0].lineNo()).toEqual(lineNumber - 1);
                });
            });

            it("Expanding code clears the folded region in editor", function () {
                runs(function () {
                    foldCodeOnLine(1);
                });
                runs(function () {
                    expandCodeOnLine(1);
                });

                runs(function () {
                    var marks = getEditorFoldMarks();
                    expect(marks.length).toEqual(0);
                });
            });

            it("folded lines have a folded marker in the gutter", function () {
                var lineNumbers = [1, 9];
                runs(function () {
                    lineNumbers.forEach(function (l) {
                        foldCodeOnLine(l);
                    });
                });

                runs(function () {
                    var marks = getGutterFoldMarks().filter(filterFolded);
                    expect(marks.length).toEqual(lineNumbers.length);

                    var gutterNumbers = marks
                        .map(getLineNumbers);
                    expect(gutterNumbers).toEqual(toZeroIndex(lineNumbers));
                });
            });

            it("foldable lines have a foldable maker in the gutter", function () {
                var lineNumbers = [1, 9, 14, 18, 22, 24, 27];
                var marks = getGutterFoldMarks();
                var gutterNumbers = marks.filter(filterOpen)
                    .map(getLineNumbers);
                expect(gutterNumbers).toEqual(toZeroIndex(lineNumbers));
            });

            describe("Preferences work as expected", function () {
                it("Persistence of fold states works as expected", function () {
                    runs(function () {
                        foldCodeOnLine(1);
                        foldCodeOnLine(14);
                        foldCodeOnLine(18);
                    });
                    runs(function () {
                        testWindow.closeAllFiles();
                    });

                    runs(function () {
                        openTestFile(testFilePath);
                    });

                    runs(function () {
                        //expect line 1 to be folded
                        var marks = getEditorFoldMarks();
                        expect(marks.length).toEqual(3);
                    });
                });

                it("Persistence of fold states can be disabled", function () {
                    setPreference("saveFoldStates", false);
                    runs(function () {
                        foldCodeOnLine(1);
                    });
                    runs(function () {
                        testWindow.closeAllFiles();
                    });

                    runs(function () {
                        openTestFile(testFilePath);
                    });

                    runs(function () {
                        //expect line 1 to be folded
                        var marks = getEditorFoldMarks();
                        expect(marks.length).toEqual(0);
                    });
                });

                it("Minimum fold size is obeyed", function () {
                    setPreference("minFoldSize", 20000);
                    runs(function () {
                        testWindow.closeAllFiles();
                    });

                    runs(function () {
                        openTestFile(testFilePath);
                    });

                    runs(function () {
                        var marks = getGutterFoldMarks();
                        expect(marks.length).toEqual(0);
                    });
                });

                it("Code folding can be disabled", function () {
                    setPreference("enabled", false);
                    runs(function () {
                        var marks = getEditorFoldMarks();
                        expect(marks.length).toEqual(0);
                    });
                });

            });
        });
    });
});
<|endoftext|>


<commit_before>/* ------------------------------------------------------------------------- *
 * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems               *
 *                                                                           *
 * 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                                                  *
 *                                                                           *
 * http://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.                                            *
 * ------------------------------------------------------------------------- */
import loadable from '@loadable/component'
import {
  ReportColumns as DashboardIcon,
  Settings as SettingsIcon,
  Activity as WebMKSIcon,
} from 'iconoir-react'

const Dashboard = loadable(
  () => import('client/containers/Dashboard/Sunstone'),
  { ssr: false }
)
const Settings = loadable(() => import('client/containers/Settings'), {
  ssr: false,
})
const Guacamole = loadable(() => import('client/containers/Guacamole'), {
  ssr: false,
})

const WebMKS = loadable(() => import('client/containers/WebMKS'), {
  ssr: false,
})

export const PATH = {
  DASHBOARD: '/dashboard',
  SETTINGS: '/settings',
  GUACAMOLE: '/guacamole/:id/:type',
  WMKS: '/wmks/:id',
}

export const ENDPOINTS = [
  {
    label: 'Dashboard',
    path: PATH.DASHBOARD,
    sidebar: true,
    icon: DashboardIcon,
    position: 1,
    Component: Dashboard,
  },
  {
    label: 'Settings',
    path: PATH.SETTINGS,
    sidebar: true,
    icon: SettingsIcon,
    position: -1,
    Component: Settings,
  },
  {
    label: 'Guacamole',
    disabledSidebar: true,
    path: PATH.GUACAMOLE,
    Component: Guacamole,
  },
  {
    label: 'WebMKS',
    sidebar: true,
    icon: WebMKSIcon,
    path: PATH.WMKS,
    Component: WebMKS,
  },
]

/**
 * YAML file describes the information and actions available in the resource.
 *
 * @typedef {object} ResourceView - Resource view file selected in redux (auth-reducer)
 * @property {string} resource_name - Resource view name
 * @property {object} actions - Bulk actions, including dialogs
 * Which buttons are visible to operate over the resources
 * @property {object} filters - List of criteria to filter the resources
 * @property {object} info-tabs - Which info tabs are used to show extended information
 */

/**
 * @param {ResourceView[]} views - View of resources
 * @param {Array} endpoints - All endpoints of app
 * @returns {Array} Returns all endpoints available
 */
export const getEndpointsByView = (views, endpoints = []) => {
  /**
   * @param {object} route - Route from view yaml
   * @param {string} [route.path] - Pathname route
   * @returns {boolean | object} If user view yaml contains the route, return it
   */
  const hasRoutePermission = (route) => {
    // Eg: '/vm-template/instantiate' => ['vm-template', 'instantiate']
    const splittedPath = String(route?.path)
      ?.toLowerCase()
      ?.split('/')
      ?.filter(Boolean)

    const [resource, ...restOfParams] = splittedPath ?? []
    const hasParams = !!restOfParams.length

    const view = views?.find((v) => resource === v.resource_name?.toLowerCase())

    // view file not exists or not match the `resource_name`
    if (!view) return false

    return (
      // match exactly. Eg: /vm
      !hasParams ||
      // allow routes with params. Eg: /vm/:id
      restOfParams.some((p) => p.includes(':')) ||
      // first param is dialog name. Eg: /vm/create or /vm-template/instantiate
      // allow action if exists on bulk actions list
      view?.actions[`${restOfParams[0]}_dialog`]
    )
  }

  return endpoints
    .map(({ routes: subRoutes, ...restOfProps }) => {
      if (Array.isArray(subRoutes)) {
        const routes = subRoutes.filter(hasRoutePermission)

        return !!routes.length && { ...restOfProps, routes }
      }

      return hasRoutePermission(restOfProps)
    })
    .filter(Boolean)
}

export default { PATH, ENDPOINTS }
<commit_msg>M #~: Remove webmks from sidebar (#1875)<commit_after>/* ------------------------------------------------------------------------- *
 * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems               *
 *                                                                           *
 * 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                                                  *
 *                                                                           *
 * http://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.                                            *
 * ------------------------------------------------------------------------- */
import loadable from '@loadable/component'
import {
  ReportColumns as DashboardIcon,
  Settings as SettingsIcon,
} from 'iconoir-react'

const Dashboard = loadable(
  () => import('client/containers/Dashboard/Sunstone'),
  { ssr: false }
)
const Settings = loadable(() => import('client/containers/Settings'), {
  ssr: false,
})
const Guacamole = loadable(() => import('client/containers/Guacamole'), {
  ssr: false,
})

const WebMKS = loadable(() => import('client/containers/WebMKS'), {
  ssr: false,
})

export const PATH = {
  DASHBOARD: '/dashboard',
  SETTINGS: '/settings',
  GUACAMOLE: '/guacamole/:id/:type',
  WMKS: '/wmks/:id',
}

export const ENDPOINTS = [
  {
    label: 'Dashboard',
    path: PATH.DASHBOARD,
    sidebar: true,
    icon: DashboardIcon,
    position: 1,
    Component: Dashboard,
  },
  {
    label: 'Settings',
    path: PATH.SETTINGS,
    sidebar: true,
    icon: SettingsIcon,
    position: -1,
    Component: Settings,
  },
  {
    label: 'Guacamole',
    disabledSidebar: true,
    path: PATH.GUACAMOLE,
    Component: Guacamole,
  },
  {
    label: 'WebMKS',
    disabledSidebar: true,
    path: PATH.WMKS,
    Component: WebMKS,
  },
]

/**
 * YAML file describes the information and actions available in the resource.
 *
 * @typedef {object} ResourceView - Resource view file selected in redux (auth-reducer)
 * @property {string} resource_name - Resource view name
 * @property {object} actions - Bulk actions, including dialogs
 * Which buttons are visible to operate over the resources
 * @property {object} filters - List of criteria to filter the resources
 * @property {object} info-tabs - Which info tabs are used to show extended information
 */

/**
 * @param {ResourceView[]} views - View of resources
 * @param {Array} endpoints - All endpoints of app
 * @returns {Array} Returns all endpoints available
 */
export const getEndpointsByView = (views, endpoints = []) => {
  /**
   * @param {object} route - Route from view yaml
   * @param {string} [route.path] - Pathname route
   * @returns {boolean | object} If user view yaml contains the route, return it
   */
  const hasRoutePermission = (route) => {
    // Eg: '/vm-template/instantiate' => ['vm-template', 'instantiate']
    const splittedPath = String(route?.path)
      ?.toLowerCase()
      ?.split('/')
      ?.filter(Boolean)

    const [resource, ...restOfParams] = splittedPath ?? []
    const hasParams = !!restOfParams.length

    const view = views?.find((v) => resource === v.resource_name?.toLowerCase())

    // view file not exists or not match the `resource_name`
    if (!view) return false

    return (
      // match exactly. Eg: /vm
      !hasParams ||
      // allow routes with params. Eg: /vm/:id
      restOfParams.some((p) => p.includes(':')) ||
      // first param is dialog name. Eg: /vm/create or /vm-template/instantiate
      // allow action if exists on bulk actions list
      view?.actions[`${restOfParams[0]}_dialog`]
    )
  }

  return endpoints
    .map(({ routes: subRoutes, ...restOfProps }) => {
      if (Array.isArray(subRoutes)) {
        const routes = subRoutes.filter(hasRoutePermission)

        return !!routes.length && { ...restOfProps, routes }
      }

      return hasRoutePermission(restOfProps)
    })
    .filter(Boolean)
}

export default { PATH, ENDPOINTS }
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 KTH, Royal Institute of Technology (http://www.kth.se/)
 *
 * This file is part of tinywhiteboard.
 *
 * Tiny Whiteboard is free software: you can redistribute it and/or modify
 * it under the terms of a MIT style license which is included in
 * the software.
 */

tinyMCEPopup.requireLangPack();

/*
 * JavaScript object driving the whiteboard dialog.
 */
var TinyWhiteboardDialog = {
    url : null,
    span : null,
    drawing : false,
    context : null,
    history : [],
    scale : {
        x : 1,
        y : 1
    },

    getX : function (ev) {
        if (typeof ev.pageX === "undefined") {
            return ev.originalEvent.touches[0].pageX;
        } else {
            return ev.pageX;
        }
    },

    getY : function (ev) {
        if (typeof ev.pageY === "undefined") {
            return ev.originalEvent.touches[0].pageY;
        } else {
            return ev.pageY;
        }
    },

    /*
     * Start of stroke. Adds the current image to the undo history
     * and begins drawing.
     */
    drawStart : function(ev) {
        var tw = TinyWhiteboardDialog;

        tw.scale.x = ev.currentTarget.width / ev.currentTarget.offsetWidth;
        tw.scale.y = ev.currentTarget.height / ev.currentTarget.offsetHeight;

        tw.drawing = true;
        tw.history.push(tw.context.getImageData(0, 0, ev.currentTarget.offsetWidth, ev.currentTarget.offsetHeight));

        tw.context.beginPath();
        tw.context.moveTo(
            tw.scale.x * (tw.getX(ev) - $(ev.currentTarget).offset().left),
            tw.scale.y * (tw.getY(ev) - $(ev.currentTarget).offset().top));

        $("#undo").removeClass("disabled");
    },

    /*
     * Currently in stroke.
     */
    draw : function(ev) {
        var tw = TinyWhiteboardDialog,
            x = tw.scale.x * (tw.getX(ev) - $(ev.currentTarget).offset().left),
            y = tw.scale.y * (tw.getY(ev) - $(ev.currentTarget).offset().top);

        if (tw.drawing) {
            ev.preventDefault();
            tw.context.lineTo(
                tw.scale.x * (tw.getX(ev) - $(ev.currentTarget).offset().left),
                tw.scale.y * (tw.getY(ev) - $(ev.currentTarget).offset().top)
            );
            tw.context.stroke();
        }
    },

    /*
     * Finish up the current stroke.
     */
    drawEnd : function(ev) {
        var tw = TinyWhiteboardDialog;

        if (tw.drawing) {
            tw.context.closePath();
            tw.drawing = false;
        }
    },

    /*
     * Restore data from the undo history and maintain button state.
     */
    undo : function() {
        var tw = TinyWhiteboardDialog;
        if (tw.history.length) {
            tw.context.putImageData(tw.history.pop(), 0, 0);
            tw.context.save();
        }
        if (! tw.history.length) {
            $("#undo").addClass("disabled");
        }
    },

    /*
     * Set up the window and populate data from selection in editor if any.
     */
    init : function() {
        var tw = this,
            ed = tinyMCEPopup.editor,
            canvas = $("#canvas"),
            selected;

        $("#undo")
            .click(tw.undo)
            .addClass("disabled");

        canvas
            .bind('mousedown touchstart', tw.drawStart)
            .bind('mousemove touchmove', tw.draw)
            .bind('mouseup mouseleave touchleave touchend', tw.drawEnd);

        /* Chrome cursor workaround */
        $(document).bind('selectstart', function(ev) {
            ev.preventDefault();
        });

        tw.context = canvas[0].getContext('2d');
        tw.context.lineCap = "round";

        tw.selectColor("black");
        $("#pens div").click(function() {
            tw.selectColor(this.id);
        });

        tw.url = tinyMCEPopup.getWindowArg('whiteboard_url');
        tw.span = tinyMCEPopup.getWindowArg('marker');

        selected = ed.dom.get(tw.span);
        if (selected && selected.hasChildNodes()) {
            tw.context.drawImage(ed.dom.get(tw.span).firstChild, 0, 0);
        }
    },

    selectColor : function(color) {
        var tw = this,
            pen = $("#" + color);

        tw.context.strokeStyle = color;
        if (color == "white") {
            tw.context.lineWidth = 8;
        } else {
            tw.context.lineWidth = 1;
        }
        $("#pens div").removeClass("selected");
        pen.addClass("selected");
        $("#canvas")
            .css("cursor", "url(img/" + color + "-cursor.png), crosshair")
            .css("cursor:focus", "url(img/" + color + "-cursor.png), crosshair")
            .css("cursor:active", "url(img/" + color + "-cursor.png), crosshair");
    },

    /*
     * Send synchronous call to whiteboard backend service for image,
     * will update the editor if successful, indicate error states
     * otherwise.
     */
    callWhiteboard : function(img) {
        var tw = this;

        $("#inProgress").show();

        $.ajax({
                url: tw.url,
                type: 'POST',
                dataType: 'json',
                data: JSON.stringify({
                    id: $(img).attr('id'),
                    src: $(img).attr('src'),
                    width: $(img).attr('width'),
                    height: $(img).attr('height'),
                    data: $("#canvas")[0].toDataURL("image/png", 1)
                })
            })
            .done(function(d, s, xhr) {
                $("#inProgress").hide();
        		$(img)
        		  .attr('id', d.id)
        		  .attr('src', d.src)
        		  .attr('width', d.width)
        		  .attr('height', d.height)
                tw.updateEditor(img);
            })
            .fail(function(d, s, xhr) {
                alert("failed store image, please try again.");
            })
            .always(function() {
                $("#inProgress").hide();
            });
    },

    /*
     * Attempt to store the image as a data URI directly in document.
     */
    noWhiteboardFallback : function(img) {
        var tw = this;

        $(img).attr('src', $("canvas")[0].toDataURL("image/png"), 0.1);
        tw.updateEditor(img);
    },

    /*
     * Update the editor and close popup.
     */
    updateEditor : function(img) {
        var tw = this,
            ed = tinyMCEPopup.editor;

        ed.dom.setHTML(ed.dom.get(tw.span), img.outerHTML);
        ed.execCommand('mceRepaint', false);
        tinyMCEPopup.close();
    },

    /*
     * Indicates whether there is ongoing fetch activity in UI or not.
     */
    inProgress : function(inProgress) {
        $("#inProgress").toggle(inProgress);
    },

    /*
     * Callback for the insert button in dialog.
     */
    insert : function() {
        var tw = this,
            ed = tinyMCEPopup.editor,
            canvas = $("canvas"),
            img = ed.dom.create('img', {
                'alt' : 'Whiteboard image',
                'class' : 'whiteboard',
                'width' : canvas.width(),
                'height' : canvas.height()
            });

        if (tw.url != null) {
            tw.callWhiteboard(img);
        } else {
            tw.noWhiteboardFallback(img);
        }
    }
};

tinyMCEPopup.onInit.add(TinyWhiteboardDialog.init, TinyWhiteboardDialog);
<commit_msg>Remove cruft.<commit_after>/*
 * Copyright (C) 2012 KTH, Royal Institute of Technology (http://www.kth.se/)
 *
 * This file is part of tinywhiteboard.
 *
 * Tiny Whiteboard is free software: you can redistribute it and/or modify
 * it under the terms of a MIT style license which is included in
 * the software.
 */

tinyMCEPopup.requireLangPack();

/*
 * JavaScript object driving the whiteboard dialog.
 */
var TinyWhiteboardDialog = {
    url : null,
    span : null,
    drawing : false,
    context : null,
    history : [],
    scale : {
        x : 1,
        y : 1
    },

    getX : function (ev) {
        if (typeof ev.pageX === "undefined") {
            return ev.originalEvent.touches[0].pageX;
        } else {
            return ev.pageX;
        }
    },

    getY : function (ev) {
        if (typeof ev.pageY === "undefined") {
            return ev.originalEvent.touches[0].pageY;
        } else {
            return ev.pageY;
        }
    },

    /*
     * Start of stroke. Adds the current image to the undo history
     * and begins drawing.
     */
    drawStart : function(ev) {
        var tw = TinyWhiteboardDialog;

        tw.scale.x = ev.currentTarget.width / ev.currentTarget.offsetWidth;
        tw.scale.y = ev.currentTarget.height / ev.currentTarget.offsetHeight;

        tw.drawing = true;
        tw.history.push(tw.context.getImageData(0, 0, ev.currentTarget.offsetWidth, ev.currentTarget.offsetHeight));

        tw.context.beginPath();
        tw.context.moveTo(
            tw.scale.x * (tw.getX(ev) - $(ev.currentTarget).offset().left),
            tw.scale.y * (tw.getY(ev) - $(ev.currentTarget).offset().top));

        $("#undo").removeClass("disabled");
    },

    /*
     * Currently in stroke.
     */
    draw : function(ev) {
        var tw = TinyWhiteboardDialog;

        if (tw.drawing) {
            ev.preventDefault();
            tw.context.lineTo(
                tw.scale.x * (tw.getX(ev) - $(ev.currentTarget).offset().left),
                tw.scale.y * (tw.getY(ev) - $(ev.currentTarget).offset().top)
            );
            tw.context.stroke();
        }
    },

    /*
     * Finish up the current stroke.
     */
    drawEnd : function(ev) {
        var tw = TinyWhiteboardDialog;

        if (tw.drawing) {
            tw.context.closePath();
            tw.drawing = false;
        }
    },

    /*
     * Restore data from the undo history and maintain button state.
     */
    undo : function() {
        var tw = TinyWhiteboardDialog;
        if (tw.history.length) {
            tw.context.putImageData(tw.history.pop(), 0, 0);
            tw.context.save();
        }
        if (! tw.history.length) {
            $("#undo").addClass("disabled");
        }
    },

    /*
     * Set up the window and populate data from selection in editor if any.
     */
    init : function() {
        var tw = this,
            ed = tinyMCEPopup.editor,
            canvas = $("#canvas"),
            selected;

        $("#undo")
            .click(tw.undo)
            .addClass("disabled");

        canvas
            .bind('mousedown touchstart', tw.drawStart)
            .bind('mousemove touchmove', tw.draw)
            .bind('mouseup mouseleave touchleave touchend', tw.drawEnd);

        /* Chrome cursor workaround */
        $(document).bind('selectstart', function(ev) {
            ev.preventDefault();
        });

        tw.context = canvas[0].getContext('2d');
        tw.context.lineCap = "round";

        tw.selectColor("black");
        $("#pens div").click(function() {
            tw.selectColor(this.id);
        });

        tw.url = tinyMCEPopup.getWindowArg('whiteboard_url');
        tw.span = tinyMCEPopup.getWindowArg('marker');

        selected = ed.dom.get(tw.span);
        if (selected && selected.hasChildNodes()) {
            tw.context.drawImage(ed.dom.get(tw.span).firstChild, 0, 0);
        }
    },

    selectColor : function(color) {
        var tw = this,
            pen = $("#" + color);

        tw.context.strokeStyle = color;
        if (color == "white") {
            tw.context.lineWidth = 8;
        } else {
            tw.context.lineWidth = 1;
        }
        $("#pens div").removeClass("selected");
        pen.addClass("selected");
        $("#canvas")
            .css("cursor", "url(img/" + color + "-cursor.png), crosshair")
            .css("cursor:focus", "url(img/" + color + "-cursor.png), crosshair")
            .css("cursor:active", "url(img/" + color + "-cursor.png), crosshair");
    },

    /*
     * Send synchronous call to whiteboard backend service for image,
     * will update the editor if successful, indicate error states
     * otherwise.
     */
    callWhiteboard : function(img) {
        var tw = this;

        $("#inProgress").show();

        $.ajax({
                url: tw.url,
                type: 'POST',
                dataType: 'json',
                data: JSON.stringify({
                    id: $(img).attr('id'),
                    src: $(img).attr('src'),
                    width: $(img).attr('width'),
                    height: $(img).attr('height'),
                    data: $("#canvas")[0].toDataURL("image/png", 1)
                })
            })
            .done(function(d, s, xhr) {
                $("#inProgress").hide();
        		$(img)
        		  .attr('id', d.id)
        		  .attr('src', d.src)
        		  .attr('width', d.width)
        		  .attr('height', d.height)
                tw.updateEditor(img);
            })
            .fail(function(d, s, xhr) {
                alert("failed store image, please try again.");
            })
            .always(function() {
                $("#inProgress").hide();
            });
    },

    /*
     * Attempt to store the image as a data URI directly in document.
     */
    noWhiteboardFallback : function(img) {
        var tw = this;

        $(img).attr('src', $("canvas")[0].toDataURL("image/png"), 0.1);
        tw.updateEditor(img);
    },

    /*
     * Update the editor and close popup.
     */
    updateEditor : function(img) {
        var tw = this,
            ed = tinyMCEPopup.editor;

        ed.dom.setHTML(ed.dom.get(tw.span), img.outerHTML);
        ed.execCommand('mceRepaint', false);
        tinyMCEPopup.close();
    },

    /*
     * Indicates whether there is ongoing fetch activity in UI or not.
     */
    inProgress : function(inProgress) {
        $("#inProgress").toggle(inProgress);
    },

    /*
     * Callback for the insert button in dialog.
     */
    insert : function() {
        var tw = this,
            ed = tinyMCEPopup.editor,
            canvas = $("canvas"),
            img = ed.dom.create('img', {
                'alt' : 'Whiteboard image',
                'class' : 'whiteboard',
                'width' : canvas.width(),
                'height' : canvas.height()
            });

        if (tw.url != null) {
            tw.callWhiteboard(img);
        } else {
            tw.noWhiteboardFallback(img);
        }
    }
};

tinyMCEPopup.onInit.add(TinyWhiteboardDialog.init, TinyWhiteboardDialog);
<|endoftext|>


<commit_before>/* ------------------------------------------------------------------------- *
 * Copyright 2002-2022, OpenNebula Project, OpenNebula Systems               *
 *                                                                           *
 * 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                                                  *
 *                                                                           *
 * http://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.                                            *
 * ------------------------------------------------------------------------- */
import { Actions, Commands } from 'server/routes/api/auth/routes'

import { actions as authActions } from 'client/features/Auth/slice'
import { dismissSnackbar } from 'client/features/General/actions'
import { oneApi, ONE_RESOURCES_POOL } from 'client/features/OneApi'
import userApi from 'client/features/OneApi/user'

import { storage } from 'client/utils'
import { JWT_NAME, FILTER_POOL, ONEADMIN_ID } from 'client/constants'

const { GROUP_POOL, ...restOfPool } = ONE_RESOURCES_POOL
const { ALL_RESOURCES, PRIMARY_GROUP_RESOURCES } = FILTER_POOL

const authApi = oneApi.injectEndpoints({
  endpoints: (builder) => ({
    getAuthUser: builder.query({
      /**
       * @returns {object} Information about authenticated user
       * @throws Fails when response isn't code 200
       */
      query: () => ({ command: { path: '/user/info' } }),
      transformResponse: (response) => response?.USER,
      async onQueryStarted(_, { queryFulfilled, dispatch }) {
        try {
          const { data: user } = await queryFulfilled
          dispatch(authActions.changeAuthUser(user))
        } catch {}
      },
    }),
    login: builder.mutation({
      /**
       * Login in the interface.
       *
       * @param {object} params - User credentials
       * @param {string} params.user - Username
       * @param {string} params.token - Password
       * @param {boolean} [params.remember] - Remember session
       * @param {string} [params.token2fa] - Token for Two factor authentication
       * @returns {object} Response data from request
       * @throws Fails when response isn't code 200
       */
      query: (params) => {
        const name = Actions.AUTHENTICATION
        const command = { name, ...Commands[name] }

        return { params, command, needState: true }
      },
      transformResponse: (response, meta) => {
        const { id, token } = response
        const { withGroupSwitcher } = meta?.state?.general ?? {}
        const isOneAdmin = id === ONEADMIN_ID

        return {
          jwt: token,
          user: { ID: id },
          isLoginInProgress: withGroupSwitcher && !!token && !isOneAdmin,
        }
      },
      async onQueryStarted(_, { queryFulfilled, dispatch }) {
        try {
          const { data: queryData } = await queryFulfilled
          const { jwt, ...user } = queryData

          if (jwt) {
            storage(JWT_NAME, jwt)
            dispatch(dismissSnackbar({ dismissAll: true }))
          }

          dispatch(authActions.changeJwt(queryData))
          dispatch(authActions.changeAuthUser(user))
        } catch {}
      },
    }),
    changeAuthGroup: builder.mutation({
      /**
       * @param {object} data - User credentials
       * @param {string} data.group - Group id
       * @returns {Promise} Response data from request
       * @throws Fails when response isn't code 200
       */
      queryFn: async ({ group } = {}, { getState, dispatch }) => {
        try {
          if (group === ALL_RESOURCES) {
            dispatch(authActions.changeFilterPool(ALL_RESOURCES))

            return { data: '' }
          }

          const authUser = getState().auth.user
          const queryData = { id: authUser.ID, group }

          const newGroup = await dispatch(
            userApi.endpoints.changeGroup.initiate(queryData)
          ).unwrap()

          dispatch(authActions.changeFilterPool(PRIMARY_GROUP_RESOURCES))
          dispatch(authActions.changeAuthUser({ GID: `${group}` }))

          return { data: newGroup }
        } catch (error) {
          return { error }
        }
      },
      invalidatesTags: [...Object.values(restOfPool)],
    }),
  }),
})

export const {
  // Queries
  useGetAuthUserQuery,
  useLazyGetAuthUserQuery,

  // Mutations
  useLoginMutation,
  useChangeAuthGroupMutation,
} = authApi

export default authApi
<commit_msg>M #~: Minor fix on api auth (#2100)<commit_after>/* ------------------------------------------------------------------------- *
 * Copyright 2002-2022, OpenNebula Project, OpenNebula Systems               *
 *                                                                           *
 * 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                                                  *
 *                                                                           *
 * http://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.                                            *
 * ------------------------------------------------------------------------- */
import { Actions, Commands } from 'server/routes/api/auth/routes'

import { actions as authActions } from 'client/features/Auth/slice'
import { dismissSnackbar } from 'client/features/General/actions'
import { oneApi, ONE_RESOURCES_POOL } from 'client/features/OneApi'
import userApi from 'client/features/OneApi/user'

import { storage } from 'client/utils'
import { JWT_NAME, FILTER_POOL, ONEADMIN_ID } from 'client/constants'

const { GROUP_POOL, ...restOfPool } = ONE_RESOURCES_POOL
const { ALL_RESOURCES, PRIMARY_GROUP_RESOURCES } = FILTER_POOL

const authApi = oneApi.injectEndpoints({
  endpoints: (builder) => ({
    getAuthUser: builder.query({
      /**
       * @returns {object} Information about authenticated user
       * @throws Fails when response isn't code 200
       */
      query: () => ({ command: { path: '/user/info' } }),
      transformResponse: (response) => response?.USER,
      async onQueryStarted(_, { queryFulfilled, dispatch }) {
        try {
          const { data: user } = await queryFulfilled
          dispatch(authActions.changeAuthUser(user))
        } catch {}
      },
    }),
    login: builder.mutation({
      /**
       * Login in the interface.
       *
       * @param {object} params - User credentials
       * @param {string} params.user - Username
       * @param {string} params.token - Password
       * @param {boolean} [params.remember] - Remember session
       * @param {string} [params.token2fa] - Token for Two factor authentication
       * @returns {object} Response data from request
       * @throws Fails when response isn't code 200
       */
      query: (params) => {
        const name = Actions.AUTHENTICATION
        const command = { name, ...Commands[name] }

        return { params, command, needState: true }
      },
      transformResponse: (response, meta) => {
        const { id, token } = response
        const { withGroupSwitcher } = meta?.state?.general ?? {}
        const isOneAdmin = id === ONEADMIN_ID

        return {
          jwt: token,
          user: { ID: id },
          isLoginInProgress: withGroupSwitcher && !!token && !isOneAdmin,
        }
      },
      async onQueryStarted(_, { queryFulfilled, dispatch }) {
        try {
          const { data: queryData } = await queryFulfilled
          const { jwt, ...user } = queryData

          if (jwt) {
            storage(JWT_NAME, jwt)
            dispatch(dismissSnackbar({ dismissAll: true }))
          }

          dispatch(authActions.changeJwt(jwt))
          dispatch(authActions.changeAuthUser(user))
        } catch {}
      },
    }),
    changeAuthGroup: builder.mutation({
      /**
       * @param {object} data - User credentials
       * @param {string} data.group - Group id
       * @returns {Promise} Response data from request
       * @throws Fails when response isn't code 200
       */
      queryFn: async ({ group } = {}, { getState, dispatch }) => {
        try {
          if (group === ALL_RESOURCES) {
            dispatch(authActions.changeFilterPool(ALL_RESOURCES))

            return { data: '' }
          }

          const authUser = getState().auth.user
          const queryData = { id: authUser.ID, group }

          const newGroup = await dispatch(
            userApi.endpoints.changeGroup.initiate(queryData)
          ).unwrap()

          dispatch(authActions.changeFilterPool(PRIMARY_GROUP_RESOURCES))
          dispatch(authActions.changeAuthUser({ GID: `${group}` }))

          return { data: newGroup }
        } catch (error) {
          return { error }
        }
      },
      invalidatesTags: [...Object.values(restOfPool)],
    }),
  }),
})

export const {
  // Queries
  useGetAuthUserQuery,
  useLazyGetAuthUserQuery,

  // Mutations
  useLoginMutation,
  useChangeAuthGroupMutation,
} = authApi

export default authApi
<|endoftext|>


<commit_before>
var wffWS = new function() {

	var encoder = wffGlobal.encoder;
	var decoder = wffGlobal.decoder;

	//last reconnect interval obj
	var prevIntvl = null;
	var webSocket;
	var inDataQ = [];
	var sendQData;

	this.openSocket = function(wsUrl) {

		// Ensures only one connection is open at a time
		if (typeof webSocket !== 'undefined'
				&& webSocket.readyState !== WebSocket.CLOSED
				&& webSocket.readyState !== WebSocket.CLOSING) {
			console.log("WebSocket is already opened.");
			return;
		}

		// Create a new instance of websocket
		webSocket = new WebSocket(wsUrl);

		sendQData = function() {
			if (webSocket.readyState === WebSocket.OPEN) {
				var inData = [];
				var ndx = 0;
				var xp = false;
				for (ndx = 0; ndx < inDataQ.length; ndx++) {
					var each = inDataQ[ndx];
					if (!xp) {
						try {
							webSocket.send(new Int8Array(each).buffer);
						} catch(e) {
							xp = true;
							inData.push(each);
						}
					} else {
						inData.push(each);
					}
				}
				inDataQ = inData;
			}
		};

		// this is required to send binary data
		webSocket.binaryType = 'arraybuffer';

		webSocket.onopen = function(event) {
			try {

				if(prevIntvl !== null) {
					clearInterval(prevIntvl);
					prevIntvl = null;
				}

				wffBMClientEvents.wffRemovePrevBPInstance();

				sendQData();

				if (typeof event.data === 'undefined') {
					return;
				}

				var binary = new Int8Array(event.data);

				if (binary.length < 4) {
					//invalid wff bm message so not to process
					return;
				}

				wffClientCRUDUtil.invokeTasks(binary);
			}catch(e){
				wffLog(e);
			}
		};

		webSocket.onmessage = function(event) {
			try {
				var binary = new Int8Array(event.data);
				console.log(binary);

				if (binary.length < 4) {
					//invalid wff bm message so not to process
					return;
				}

				// for (var i = 0; i < binary.length; i++) {
				// console.log(i, binary[i]);
				// }

				var executed = wffClientMethods.exePostFun(binary);

				if (!executed) {
					wffClientCRUDUtil.invokeTasks(binary);
				}

				// var wffMessage = wffBMUtil.parseWffBinaryMessageBytes(binary);
				// console.log('wffMessage', wffMessage);
				//
				// for (var i = 0; i < wffMessage.length; i++) {
				// var decodedString = decoder.decode(new Uint8Array(
				// wffMessage[i].name));
				// console.log('decodedString', decodedString,
				// 'wffMessage[i].values.length', wffMessage[i].values.length);
				// console.log('values');
				// for (var j = 0; j < wffMessage[i].values.length; j++) {
				// console.log('value', decoder.decode(new Uint8Array(
				// wffMessage[i].values[j])));
				// }
				// }

				// var string = new TextDecoder("utf-8").decode(binary);
				// console.log(string, string);

				// var binary = new Uint8Array(event.data.length);
				// for (var i = 0; i < event.data.length; i++) {
				// console.log(i, event.data[i]);
				// // binary[i] = event.data[i];
				// }
				// console.dir(binary);
				// sendBinary(binary.buffer);

				// console.log('going to writeResponse');
				// writeResponse(event.data);

				// var uint8array = new TextEncoder("utf-8").encode("¢");
				// var string = new TextDecoder("utf-8").decode(uint8array);
			}catch(e){
				wffLog(e);
			}
		};

		webSocket.onclose = function(event) {
			if(prevIntvl !== null) {
				clearInterval(prevIntvl);
				prevIntvl = null;
			}
			prevIntvl = setInterval(function() {
				if (typeof webSocket === 'undefined' || webSocket.readyState === WebSocket.CLOSED) {
					wffWS.openSocket(wffGlobal.WS_URL);
				}
			}, wffGlobal.WS_RECON);
		};
		webSocket.onerror = function(event) {
			try{webSocket.close();}catch(e){wffLog("ws.close error");}
		};
	};

	/**
	 * Sends the bytes to the server
	 */
	this.send = function(bytes) {
		if (bytes.length > 0) {
			inDataQ.push(bytes);
			sendQData();
		} else {
			webSocket.send(new Int8Array(bytes).buffer);
		}
	};

	this.closeSocket = function() {
		try {
			if (typeof webSocket !== 'undefined'
				&& webSocket.readyState !== WebSocket.CONNECTING
				&& webSocket.readyState !== WebSocket.CLOSED) {
				webSocket.close();
			}
		} catch(e){}
	};

};
<commit_msg>Implemented wffWS.getStatus function<commit_after>
var wffWS = new function() {

	var encoder = wffGlobal.encoder;
	var decoder = wffGlobal.decoder;

	//last reconnect interval obj
	var prevIntvl = null;
	var webSocket;
	var inDataQ = [];
	var sendQData;

	this.openSocket = function(wsUrl) {

		// Ensures only one connection is open at a time
		if (typeof webSocket !== 'undefined'
				&& webSocket.readyState !== WebSocket.CLOSED
				&& webSocket.readyState !== WebSocket.CLOSING) {
			console.log("WebSocket is already opened.");
			return;
		}

		// Create a new instance of websocket
		webSocket = new WebSocket(wsUrl);

		sendQData = function() {
			if (webSocket.readyState === WebSocket.OPEN) {
				var inData = [];
				var ndx = 0;
				var xp = false;
				for (ndx = 0; ndx < inDataQ.length; ndx++) {
					var each = inDataQ[ndx];
					if (!xp) {
						try {
							webSocket.send(new Int8Array(each).buffer);
						} catch(e) {
							xp = true;
							inData.push(each);
						}
					} else {
						inData.push(each);
					}
				}
				inDataQ = inData;
			}
		};

		// this is required to send binary data
		webSocket.binaryType = 'arraybuffer';

		webSocket.onopen = function(event) {
			try {

				if(prevIntvl !== null) {
					clearInterval(prevIntvl);
					prevIntvl = null;
				}

				wffBMClientEvents.wffRemovePrevBPInstance();

				sendQData();

				if (typeof event.data === 'undefined') {
					return;
				}

				var binary = new Int8Array(event.data);

				if (binary.length < 4) {
					//invalid wff bm message so not to process
					return;
				}

				wffClientCRUDUtil.invokeTasks(binary);
			}catch(e){
				wffLog(e);
			}
		};

		webSocket.onmessage = function(event) {
			try {
				var binary = new Int8Array(event.data);
				console.log(binary);

				if (binary.length < 4) {
					//invalid wff bm message so not to process
					return;
				}

				// for (var i = 0; i < binary.length; i++) {
				// console.log(i, binary[i]);
				// }

				var executed = wffClientMethods.exePostFun(binary);

				if (!executed) {
					wffClientCRUDUtil.invokeTasks(binary);
				}

				// var wffMessage = wffBMUtil.parseWffBinaryMessageBytes(binary);
				// console.log('wffMessage', wffMessage);
				//
				// for (var i = 0; i < wffMessage.length; i++) {
				// var decodedString = decoder.decode(new Uint8Array(
				// wffMessage[i].name));
				// console.log('decodedString', decodedString,
				// 'wffMessage[i].values.length', wffMessage[i].values.length);
				// console.log('values');
				// for (var j = 0; j < wffMessage[i].values.length; j++) {
				// console.log('value', decoder.decode(new Uint8Array(
				// wffMessage[i].values[j])));
				// }
				// }

				// var string = new TextDecoder("utf-8").decode(binary);
				// console.log(string, string);

				// var binary = new Uint8Array(event.data.length);
				// for (var i = 0; i < event.data.length; i++) {
				// console.log(i, event.data[i]);
				// // binary[i] = event.data[i];
				// }
				// console.dir(binary);
				// sendBinary(binary.buffer);

				// console.log('going to writeResponse');
				// writeResponse(event.data);

				// var uint8array = new TextEncoder("utf-8").encode("¢");
				// var string = new TextDecoder("utf-8").decode(uint8array);
			}catch(e){
				wffLog(e);
			}
		};

		webSocket.onclose = function(event) {
			if(prevIntvl !== null) {
				clearInterval(prevIntvl);
				prevIntvl = null;
			}
			prevIntvl = setInterval(function() {
				if (typeof webSocket === 'undefined' || webSocket.readyState === WebSocket.CLOSED) {
					wffWS.openSocket(wffGlobal.WS_URL);
				}
			}, wffGlobal.WS_RECON);
		};
		webSocket.onerror = function(event) {
			try{webSocket.close();}catch(e){wffLog("ws.close error");}
		};
	};

	/**
	 * Sends the bytes to the server
	 */
	this.send = function(bytes) {
		if (bytes.length > 0) {
			inDataQ.push(bytes);
			sendQData();
		} else {
			webSocket.send(new Int8Array(bytes).buffer);
		}
	};

	this.closeSocket = function() {
		try {
			if (typeof webSocket !== 'undefined'
				&& webSocket.readyState !== WebSocket.CONNECTING
				&& webSocket.readyState !== WebSocket.CLOSED) {
				webSocket.close();
			}
		} catch(e){}
	};

	this.getStatus = function() {
		if (typeof webSocket !== 'undefined') {
			return webSocket.readyState;
		}
		return -1;
	};
};
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics.v2.userfeedback',
  name: 'UserFeedback',
  documentation: `
    A model track of feedback messages as an object travels
    through multiple decorators on the back-end and
    inevitably making its way back to the client who views the feedback
  `,

  properties: [
    {
      class: 'Enum',
      name: 'status',
      of: 'foam.comics.v2.userfeedback.UserFeedbackStatus'
    },
    {
      class: 'String',
      name: 'message'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.userfeedback.UserFeedback',
      name: 'next',
      javaToCSV: `
        outputter.outputValue("next");
      `,
      javaToCSVLabel: `
      //to remove "infinite" loop
      //but need to come up with logic here
        outputter.outputValue("");
      `
    }
  ],
});
<commit_msg>UserFeedback changes<commit_after>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics.v2.userfeedback',
  name: 'UserFeedback',
  documentation: `
    A model track of feedback messages as an object travels
    through multiple decorators on the back-end and
    inevitably making its way back to the client who views the feedback
  `,

  properties: [
    {
      class: 'Enum',
      name: 'status',
      of: 'foam.comics.v2.userfeedback.UserFeedbackStatus'
    },
    {
      class: 'String',
      name: 'message'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.userfeedback.UserFeedback',
      name: 'next',
      javaToCSV: ``,
      javaToCSVLabel: ``
    }
  ],
});
<|endoftext|>


<commit_before>'use strict';
var _ = require('lodash');

var themeFunctions = require('theme/functions') || {};

var MdHelper = require('./elements/MdHelper');
var postHooks = require('./postHooks');
var config = require('config');

function allPosts() {
  var returnObj = {};

  var posts = _.map(postReq().keys(), function(name) {
    return [
      name,
      postReq()(name),
    ];
  });

  // Include drafts if we're not in prod
  var drafts = [];
  if(__DEV__) {
    var req = draftReq();
    if(req) {
      drafts = _.map(req.keys(), function(name) {
        return [
          name,
          _.assign({draft: true}, draftReq()(name)),
        ];
      });
    }
  }

  // TODO: build plugin arch here
  posts = generateNextPrev(posts);
  drafts = generateNextPrev(drafts);

  posts = postHooks.preProcessPosts(posts);

  // Build some nice objects from the files
  _.each(posts.concat(drafts), function(fileArr) {
    var post = fileArr[1];
    var fileName = fileArr[0].slice(2); // remove the './'

    // Name is on format ./YYYY-MM-DD-url_title.md
    // TODO: Configurable file name standard
    var processedFile = processPost(
      post,
      fileName
    );

    returnObj[processedFile.url] = processedFile;
  });
  returnObj = postHooks.postProcessPosts(returnObj);

  return returnObj;
}
exports.allPosts = allPosts;

// TODO: push into a plugin
function generateNextPrev(posts) {
  var len = posts.length;

  return posts.map(function(fileArr, i) {
    var post = fileArr[1];

    if(i > 0) {
      post.prev = posts[1][i - 1];
    }
    if(i < len - 2) {
      post.next = posts[1][i + 1];
    }

    return fileArr;
  });
}

function allPages() {
  // TODO: allow hooks on page processing
  var req = pageReq();
  var pages = {};

  _.each(req.keys(), function(name) {
    // name is format ./url_title.ext
    var file = req(name); // require the file
    var fileName = name.slice(2); // remove the './'

    var content = renderContent(file);

    // url is filename minus extension
    var url = _.kebabCase(fileName.split('.')[0]);

    // title is the capitalized url
    var title = _.capitalize(url.replace(/\-/g, ' '));

    // rewrite the index file
    if(url === 'index') {
      url = '/';
    }

    pages[url] = {
      url: url,
      fileName: fileName,
      title: title,
      content: content,
    };
  });

  return pages;
}
exports.allPages = allPages;

function postForPath(path) {
  return allPosts()[path];
}
exports.postForPath = postForPath;

function pageForPath(path) {
  return allPages()[path];
}
exports.pageForPath = pageForPath;

function pageReq() {
  return require.context('pages', false);
}
exports.pageReq = pageReq;

function postReq() {
  return require.context('posts', false, /^\.\/.*\.md$/);
}
exports.postReq = postReq;

function draftReq() {
  try {
    return require.context('drafts', false, /^\.\/.*\.md$/);
  }
  catch(e) {}
}
exports.draftReq = draftReq;

function renderContent(content) {
  return MdHelper.render(content);
}
exports.renderContent = renderContent;

function processPost(file, fileName) {
  // TODO: implement nicer hooks to configurable functions

  // clean the filename to get the url
  var url;
  if(config.site.functions && config.site.functions.url) {
    url = config.site.functions.url(file, fileName);
  }
  else if(themeFunctions.url) {
    url = themeFunctions.url(file, fileName);
  }
  else {
    // XXXXX: use a path opt to cut the ext instead?
    url = fileName.slice(0, fileName.length - 3);
  }

  // get the date from the file name if it's not in the frontmatter
  var date;
  if(config.site.functions && config.site.functions.date) {
    date = config.site.functions.date(file, fileName);
  }
  else if(themeFunctions.date) {
    date = themeFunctions.date(file, fileName);
  }
  else {
    date = file.date || fileName.slice(0, 10);
  }

  // get the content
  var content;
  if(config.site.functions && config.site.functions.content) {
    content = config.site.functions.content(file, fileName);
  }
  else if(themeFunctions.content) {
    content = themeFunctions.content(file, fileName);
  }
  else {
    content = MdHelper.render(file.__content);
  }

  // generate the preview
  var preview;
  if(config.site.functions && config.site.functions.preview) {
    preview = config.site.functions.preview(file, fileName);
  }
  else if(themeFunctions.preview) {
    preview = themeFunctions.preview(file, fileName);
  }
  else {
    preview = file.preview || MdHelper.getContentPreview(file.__content);
  }

  return _.assign({}, file, {
    url: url,
    content: content,
    date: date,
    preview: preview
  });
}
<commit_msg>Cleaner post function hooks<commit_after>'use strict';
var _ = require('lodash');

var themeFunctions = require('theme/functions') || {};

var MdHelper = require('./elements/MdHelper');
var postHooks = require('./postHooks');
var siteFunctions = require('config').site.functions || {} ;

function allPosts() {
  var returnObj = {};

  var posts = _.map(postReq().keys(), function(name) {
    return [
      name,
      postReq()(name),
    ];
  });

  // Include drafts if we're not in prod
  var drafts = [];
  if(__DEV__) {
    var req = draftReq();
    if(req) {
      drafts = _.map(req.keys(), function(name) {
        return [
          name,
          _.assign({draft: true}, draftReq()(name)),
        ];
      });
    }
  }

  // TODO: build plugin arch here
  posts = generateNextPrev(posts);
  drafts = generateNextPrev(drafts);

  posts = postHooks.preProcessPosts(posts);

  // Build some nice objects from the files
  _.each(posts.concat(drafts), function(fileArr) {
    var post = fileArr[1];
    var fileName = fileArr[0].slice(2); // remove the './'

    // Name is on format ./YYYY-MM-DD-url_title.md
    // TODO: Configurable file name standard
    var processedFile = processPost(
      post,
      fileName
    );

    returnObj[processedFile.url] = processedFile;
  });
  returnObj = postHooks.postProcessPosts(returnObj);

  return returnObj;
}
exports.allPosts = allPosts;

// TODO: push into a plugin
function generateNextPrev(posts) {
  var len = posts.length;

  return posts.map(function(fileArr, i) {
    var post = fileArr[1];

    if(i > 0) {
      post.prev = posts[1][i - 1];
    }
    if(i < len - 2) {
      post.next = posts[1][i + 1];
    }

    return fileArr;
  });
}

function allPages() {
  // TODO: allow hooks on page processing
  var req = pageReq();
  var pages = {};

  _.each(req.keys(), function(name) {
    // name is format ./url_title.ext
    var file = req(name); // require the file
    var fileName = name.slice(2); // remove the './'

    var content = renderContent(file);

    // url is filename minus extension
    var url = _.kebabCase(fileName.split('.')[0]);

    // title is the capitalized url
    var title = _.capitalize(url.replace(/\-/g, ' '));

    // rewrite the index file
    if(url === 'index') {
      url = '/';
    }

    pages[url] = {
      url: url,
      fileName: fileName,
      title: title,
      content: content,
    };
  });

  return pages;
}
exports.allPages = allPages;

function postForPath(path) {
  return allPosts()[path];
}
exports.postForPath = postForPath;

function pageForPath(path) {
  return allPages()[path];
}
exports.pageForPath = pageForPath;

function pageReq() {
  return require.context('pages', false);
}
exports.pageReq = pageReq;

function postReq() {
  return require.context('posts', false, /^\.\/.*\.md$/);
}
exports.postReq = postReq;

function draftReq() {
  try {
    return require.context('drafts', false, /^\.\/.*\.md$/);
  }
  catch(e) {}
}
exports.draftReq = draftReq;

function renderContent(content) {
  return MdHelper.render(content);
}
exports.renderContent = renderContent;

function processPost(file, fileName) {
  // TODO: implement nicer hooks to configurable functions
  var functions = _.assign({
    url: function(file, fileName) {
      return fileName.slice(0, fileName.length - 3);
    },
    date: function(file, fileName) {
      return file.date || fileName.slice(0, 10);
    },
    preview: function(file, fileName) {
      return file.preview || MdHelper.getContentPreview(file.__content);
    },
    content: function(file, fileName) {
      return MdHelper.render(file.__content);
    },
  }, themeFunctions, siteFunctions);

  var url = functions.url(file, fileName);
  var date = functions.date(file, fileName);
  var preview = functions.preview(file, fileName);
  var content = functions.content(file, fileName);

  return _.assign({}, file, {
    url: url,
    content: content,
    date: date,
    preview: preview
  });
}
<|endoftext|>


<commit_before>import {
  describe,
  it,
  before,
  after
} from 'mocha';
import { expect } from 'chai';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';

describe('Acceptance | 32 - Créer une épreuve de type QCU | ', function () {

  let application;
  let challenge;

  before(function () {
    application = startApp();
    challenge = server.create('challenge-airtable');
  });

  after(function () {
    destroyApp(application);
  });

  describe('Prévisualiser une épreuve |', function () {

    let challengeId;

    before(function () {
      challengeId  = challenge.attrs.id;
      return visit(`/challenges/${challengeId}/preview`);
    });

    it('Il est possible de prévisualiser une épreuve en accédant à l\'URL /challenges/:id/preview', function () {
      expect(currentURL()).to.equal(`/challenges/${challengeId}/preview`);
    });

    describe('On affiche', function () {

      let $challenge;

      before(function () {
        $challenge = findWithAssert('#challenge-preview');
      });

      it('l\'identifiant de l\'épreuve', function () {
        expect($challenge.findWithAssert('.title').text()).to.contains(`Prévisualisation de l'épreuve #${challengeId}`);
      });

      it('la consigne de l\'épreuve', function () {
        expect($challenge.findWithAssert('.challenge-instruction').text()).to.contains(challenge.attrs.fields.Consigne);
      });

      // FIXME: this is not part of the US. This should be removed (need validation in PR)
      //it('les propositions sous forme de boutons radio', function () {
      //  const $proposals = findWithAssert('.challenge-proposals input[type="radio"][name="proposals"]');
      //  expect($proposals).to.have.lengthOf(5);
      //});
    });
  });
});
<commit_msg> fix bad findWithAssert<commit_after>import {
  describe,
  it,
  before,
  after
} from 'mocha';
import { expect } from 'chai';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';

describe('Acceptance | 32 - Créer une épreuve de type QCU | ', function () {

  let application;
  let challenge;

  before(function () {
    application = startApp();
    challenge = server.create('challenge-airtable');
  });

  after(function () {
    destroyApp(application);
  });

  describe('Prévisualiser une épreuve |', function () {

    let challengeId;

    before(function () {
      challengeId  = challenge.attrs.id;
      return visit(`/challenges/${challengeId}/preview`);
    });

    it('Il est possible de prévisualiser une épreuve en accédant à l\'URL /challenges/:id/preview', function () {
      expect(currentURL()).to.equal(`/challenges/${challengeId}/preview`);
    });

    describe('On affiche', function () {

      let $challenge;

      before(function () {
        $challenge = findWithAssert('#challenge-preview');
      });

      it('l\'identifiant de l\'épreuve', function () {
        expect($challenge.find('.title').text()).to.contains(`Prévisualisation de l'épreuve #${challengeId}`);
      });

      it('la consigne de l\'épreuve', function () {
        expect($challenge.find('.challenge-instruction').text()).to.contains(challenge.attrs.fields.Consigne);
      });

      // FIXME: this is not part of the US. This should be removed (need validation in PR)
      //it('les propositions sous forme de boutons radio', function () {
      //  const $proposals = findWithAssert('.challenge-proposals input[type="radio"][name="proposals"]');
      //  expect($proposals).to.have.lengthOf(5);
      //});
    });
  });
});
<|endoftext|>


<commit_before>module.exports.templateBegin = `
<!doctype html>

    <meta charset="utf-8" />
    <link rel="shortcut icon" href="/static/images/favicon.ico">
    <link rel="stylesheet" href="https://code.cdn.mozilla.net/fonts/fira.css">
    <link rel="stylesheet" href="/static/styles/main.css">
    <meta name="defaultLanguage" content="en-US">
    <meta name="availableLanguages" content="en-US">
    <meta name="viewport" content="width=device-width">
    <link rel="localization" href="/static/locales/{locale}/app.ftl">

    <title>Firefox Test Pilot</title>

    <link rel="canonical" href="http://testpilot.firefox.com/">
    <meta name="description" content="Test new Features. Give us feedback. Help build Firefox." />
    <meta property="og:description" content="Test new Features. Give us feedback. Help build Firefox." />
    <meta property="og:image" content="http://testpilot.firefox.com/static/images/thumbnail-facebook.1a45270b.png" />
    <meta property="og:locale" content="en_US" />
    <meta property="og:title" content="Firefox Test Pilot" />
    <meta property="og:type" content="website" />
    <meta property="og:url" content="http://testpilot.firefox.com/" />
    <meta name="twitter:card" content="summary" />
    <meta name="twitter:description" content="Test new Features. Give us feedback. Help build Firefox." />
    <meta name="twitter:image" content="http://testpilot.firefox.com/static/images/thumbnail-twitter.19ccbdd2.png" />
    <meta name="twitter:site" content="@FxTestPilot" />
    <meta name="twitter:title" content="Firefox Test Pilot" />

      Firefox Test Pilot

`;

module.exports.templateEnd = `

        Legal
        About Test Pilot
        Privacy
        Terms
        Cookies

`;
<commit_msg>Fix footer links on page. Fixes #1648 p=jgruen<commit_after>module.exports.templateBegin = `
<!doctype html>

    <meta charset="utf-8" />
    <link rel="shortcut icon" href="/static/images/favicon.ico">
    <link rel="stylesheet" href="https://code.cdn.mozilla.net/fonts/fira.css">
    <link rel="stylesheet" href="/static/styles/main.css">
    <meta name="defaultLanguage" content="en-US">
    <meta name="availableLanguages" content="en-US">
    <meta name="viewport" content="width=device-width">
    <link rel="localization" href="/static/locales/{locale}/app.ftl">

    <title>Firefox Test Pilot</title>

    <link rel="canonical" href="http://testpilot.firefox.com/">
    <meta name="description" content="Test new Features. Give us feedback. Help build Firefox." />
    <meta property="og:description" content="Test new Features. Give us feedback. Help build Firefox." />
    <meta property="og:image" content="http://testpilot.firefox.com/static/images/thumbnail-facebook.1a45270b.png" />
    <meta property="og:locale" content="en_US" />
    <meta property="og:title" content="Firefox Test Pilot" />
    <meta property="og:type" content="website" />
    <meta property="og:url" content="http://testpilot.firefox.com/" />
    <meta name="twitter:card" content="summary" />
    <meta name="twitter:description" content="Test new Features. Give us feedback. Help build Firefox." />
    <meta name="twitter:image" content="http://testpilot.firefox.com/static/images/thumbnail-twitter.19ccbdd2.png" />
    <meta name="twitter:site" content="@FxTestPilot" />
    <meta name="twitter:title" content="Firefox Test Pilot" />

        Firefox Test Pilot

`;

module.exports.templateEnd = `

          Legal
          About Test Pilot
          Privacy
          Terms
          Cookies

`;
<|endoftext|>