<commit_before>// For conditions of distribution and use, see copyright notice in LICENSE

engine.ImportExtension("qt.core");
engine.ImportExtension("qt.xml");

QtXmlTest();

// Loads the plugin.xml from Tundra/bin twice, first by using both plain QFile, and the second time using QFile and QXmlInputSource.
function QtXmlTest()
{
    var file = new QFile("plugins.xml");
    var errorMsg;
    var errorLine;
    var errorColumn;
    var domDocFromFile = new QDomDocument();

    // The following line should work to my knowledge, but for some (non-apparent) reason it doens't:
    // var fileOk = domDocFromFile["setContent(QIODevice,bool,QString,int,int)"](file, true, errorMsg, errorLine, errorColumn);
    // I'm suspecting it has something to do with the fact that QDomDocument doesn't inherit QObject (I have encountered some mysterious
    // behavior with e.g. QTreeWidgetItem also), and the fact that the actual C++ function implementation uses default argument values:
    // bool setContent(QIODevice *dev, bool namespaceProcessing, QString * errorMsg = 0, int * errorLine = 0, int * errorColumn = 0);
    // My guess is that that passing C-type pointers (or references for that matter) which are used for output is not simply
    // supported/understood by QtScript(Generator).
    // Probably QtScriptGenerator doesn't generate function "signatures" for all cases (the function at hand would yield four different possible signatures).
    // Hence the function call must be done with none of the "optional" arguments provided.
    // So, the following works without problems (let's try "bool setContent(QIODevice * dev)"):
    var fileOk = domDocFromFile.setContent(file);
    if (fileOk)
    {
        // Should work ok!
        // Simply dump the whole file contents to stdout.
        print("File contents:");
        print(domDocFromFile.toString());
        print("");
    }

    file.close(); // Remember to close the file handle.

    // So, the setContent function seems to be working by omitting the default arguments from the signatures
    // (http://developer.qt.nokia.com/doc/qt-4.7/qdomdocument.html) alltogether, and the following signatures/overloads should work:
    // bool setContent(const QByteArray & data, bool namespaceProcessing)
    // bool setContent(const QString & text, bool namespaceProcessing)
    // bool setContent(QIODevice * dev, bool namespaceProcessing)
    // bool setContent(QXmlInputSource * source, bool namespaceProcessing)
    // bool setContent(const QString & text)
    // bool setContent(const QByteArray & buffer)
    // bool setContent(QIODevice * dev) - which we usesd previously
    // bool setContent(QXmlInputSource * source, QXmlReader * reader)

    var file2 = new QFile("plugins.xml");
    var source = new QXmlInputSource(file2);
    var domDocFromInputSource = new QDomDocument();

    // Utilize the Qt XML/DOM API and print the names of the C++ plugins defined int the XML file individually.
    // Let's try "bool setContent(QXmlInputSource * source, bool namespaceProcessing)".
    var sourceOk = domDocFromInputSource.setContent(source, true);
    if (sourceOk)
    {
        // Should work ok!
        var pluginElems = domDocFromInputSource.elementsByTagName("plugin");
        print(pluginElems.length() + " C++ plugins found. The plugins are:");
        for(var i = 0; i < pluginElems.length(); ++i)
            print("* " + pluginElems.item(i).toElement().attribute("path"));
    }

    file2.close();
}

print("< JS qt.xml example:");

//converted the c++ in http://doc.trolltech.com/4.6/qdomdocument.html
var doc = new QDomDocument("mydocument");

//works when running from Tundra bin: ./Tundra --headless --run jsmodules/apitest/qtxml.js
var file = new QFile("plugins.xml");

//if (!file.open(QIODevice::ReadOnly))
//     return;
/*if (!doc.setContent(&file)) {
     file.close();
     //return;
}*/
doc.setContent(file);
file.close();

print("---");

// print out the element names of all elements that are direct children
 // of the outermost element.
var docElem = doc.documentElement();

var n = docElem.firstChild();
while(!n.isNull()) {
    //print(".");
    var e = n.toElement(); // try to convert the node to an element.
    if(!e.isNull()) {
        print(e.tagName()); // the node really is an element.
     }
     n = n.nextSibling();
 }

/*
 // Here we append a new element to the end of the document
 QDomElement elem = doc.createElement("img");
 elem.setAttribute("src", "myimage.png");
 docElem.appendChild(elem);
*/

/* creating - no worky for some reason?
is from http://developer.qt.nokia.com/forums/viewthread/12949 where they suspect appendChild is broken.

var doc = new QDomDocument();
var root = doc.createElement("root");
doc.appendChild(root);
root.setAttribute("attr1", "value1")

var tag = doc.createElement("tag1");
root.appendChild(tag);

print(doc);
print((doc.toString()));
*/

print(".. /qt xml example end> ");<commit_msg>Now that the xml configs are gone, make the QtXml test script use scenes/Avatar/scene.txml.<commit_after>// For conditions of distribution and use, see copyright notice in LICENSE

engine.ImportExtension("qt.core");
engine.ImportExtension("qt.xml");

QtXmlTest();

// Loads the plugin.xml from Tundra/bin twice, first by using both plain QFile, and the second time using QFile and QXmlInputSource.
function QtXmlTest()
{
    var file = new QFile("scenes/Avatar/scene.txml");
    var errorMsg;
    var errorLine;
    var errorColumn;
    var domDocFromFile = new QDomDocument();

    // The following line should work to my knowledge, but for some (non-apparent) reason it doens't:
    // var fileOk = domDocFromFile["setContent(QIODevice,bool,QString,int,int)"](file, true, errorMsg, errorLine, errorColumn);
    // I'm suspecting it has something to do with the fact that QDomDocument doesn't inherit QObject (I have encountered some mysterious
    // behavior with e.g. QTreeWidgetItem also), and the fact that the actual C++ function implementation uses default argument values:
    // bool setContent(QIODevice *dev, bool namespaceProcessing, QString * errorMsg = 0, int * errorLine = 0, int * errorColumn = 0);
    // My guess is that that passing C-type pointers (or references for that matter) which are used for output is not simply
    // supported/understood by QtScript(Generator).
    // Probably QtScriptGenerator doesn't generate function "signatures" for all cases (the function at hand would yield four different possible signatures).
    // Hence the function call must be done with none of the "optional" arguments provided.
    // So, the following works without problems (let's try "bool setContent(QIODevice * dev)"):
    var fileOk = domDocFromFile.setContent(file);
    if (fileOk)
    {
        // Should work ok!
        // Simply dump the whole file contents to stdout.
        print("File contents:");
        print(domDocFromFile.toString());
        print("");
    }

    file.close(); // Remember to close the file handle.

    // So, the setContent function seems to be working by omitting the default arguments from the signatures
    // (http://developer.qt.nokia.com/doc/qt-4.7/qdomdocument.html) alltogether, and the following signatures/overloads should work:
    // bool setContent(const QByteArray & data, bool namespaceProcessing)
    // bool setContent(const QString & text, bool namespaceProcessing)
    // bool setContent(QIODevice * dev, bool namespaceProcessing)
    // bool setContent(QXmlInputSource * source, bool namespaceProcessing)
    // bool setContent(const QString & text)
    // bool setContent(const QByteArray & buffer)
    // bool setContent(QIODevice * dev) - which we usesd previously
    // bool setContent(QXmlInputSource * source, QXmlReader * reader)

    var file2 = new QFile("scenes/Avatar/scene.txml");
    var source = new QXmlInputSource(file2);
    var domDocFromInputSource = new QDomDocument();

    // Utilize the Qt XML/DOM API and print the names of the C++ plugins defined int the XML file individually.
    // Let's try "bool setContent(QXmlInputSource * source, bool namespaceProcessing)".
    var sourceOk = domDocFromInputSource.setContent(source, true);
    if (sourceOk)
    {
        // Should work ok!
        var pluginElems = domDocFromInputSource.elementsByTagName("plugin");
        print(pluginElems.length() + " C++ plugins found. The plugins are:");
        for(var i = 0; i < pluginElems.length(); ++i)
            print("* " + pluginElems.item(i).toElement().attribute("path"));
    }

    file2.close();
}

print("< JS qt.xml example:");

//converted the c++ in http://doc.trolltech.com/4.6/qdomdocument.html
var doc = new QDomDocument("mydocument");

//works when running from Tundra bin: ./Tundra --headless --run jsmodules/apitest/qtxml.js
var file = new QFile("scenes/Avatar/scene.txml");

//if (!file.open(QIODevice::ReadOnly))
//     return;
/*if (!doc.setContent(&file)) {
     file.close();
     //return;
}*/
doc.setContent(file);
file.close();

print("---");

// print out the element names of all elements that are direct children
 // of the outermost element.
var docElem = doc.documentElement();

var n = docElem.firstChild();
while(!n.isNull()) {
    //print(".");
    var e = n.toElement(); // try to convert the node to an element.
    if(!e.isNull()) {
        print(e.tagName()); // the node really is an element.
     }
     n = n.nextSibling();
 }

/*
 // Here we append a new element to the end of the document
 QDomElement elem = doc.createElement("img");
 elem.setAttribute("src", "myimage.png");
 docElem.appendChild(elem);
*/

/* creating - no worky for some reason?
is from http://developer.qt.nokia.com/forums/viewthread/12949 where they suspect appendChild is broken.

var doc = new QDomDocument();
var root = doc.createElement("root");
doc.appendChild(root);
root.setAttribute("attr1", "value1")

var tag = doc.createElement("tag1");
root.appendChild(tag);

print(doc);
print((doc.toString()));
*/

print(".. /qt xml example end> ");<|endoftext|>


<commit_before>define([
    "zepto",
    "modules/channels/channel",
    "modules/channels/tokens"
], function ($, Channel, tokens) {
    "use strict";

    function RemoteTlkeChannel() {
    }

    RemoteTlkeChannel.prototype = new Channel();
    $.extend(RemoteTlkeChannel.prototype, {
        enterToken: function (token, context) {
            // serialize token and send packet
        },
        processPacket: function (bytes) {
            // deserialize token and prompt it
        },

        serialize: function () { throw new Error("Not implemented"); }

    });

    RemoteTlkeChannel.deserialize = function (dto) {
        throw new Error("Not implemented");
    };

    return RemoteTlkeChannel;
});<commit_msg>remoteChannel impl<commit_after>define([
    "zepto",
    "modules/channels/channel",
    "modules/channels/tokens"
], function ($, Channel, tokens) {
    "use strict";

    function RemoteTlkeChannel() {}

    RemoteTlkeChannel.prototype = new Channel();
    $.extend(RemoteTlkeChannel.prototype, {
        enterToken: function (token, context) {
            this._sendPacket(tokens.serialize(token));
        },
        processPacket: function (packet) {
            this._emitPrompt(tokens.deserialize(packet));
        },

        serialize: function () { throw new Error("Not implemented"); }

    });

    RemoteTlkeChannel.deserialize = function (dto) {
        throw new Error("Not implemented");
    };

    return RemoteTlkeChannel;
});<|endoftext|>


<commit_before>/**
 * Created by turner on 6/19/14.
 */
var cursor = (function (cursor) {

    cursor.CursorHistogram = function (binCount, maxScore) {

//        this.guid = igv.guid();
        this.bins = [];
        this.bins.length = binCount;
        this.maxCount = 0;

        this.canvasFillStyle = igv.greyScale(255);
//        this.canvasFillStyle = igv.rgbColor(255, 255, 255);
        this.minMaxfillStyle = igv.rgbaColor(64, 64, 64, 0.5);
        this.minMaxEdgefillStyle = igv.rgbaColor(32, 32, 32, 1.0);

        this.initializeBins();

        this.maxScore = maxScore;

    };

    // Methods
    cursor.CursorHistogram.prototype.initializeBins = function () {

        var i, len;
        for (i=0, len=this.bins.length; i < len; i++) {
            this.bins[i] = 0;
        }

        this.maxCount = 0;
    };

    cursor.CursorHistogram.prototype.insertScore = function (score) {

        if (score < 0) {
            return;
        }

        var index = this.scoreIndex(score);
        this.bins[ index ] += 1;
        this.maxCount = Math.max(this.maxCount, this.bins[ index ]);
    };

    cursor.CursorHistogram.prototype.scoreIndex = function (score) {

        var value;

        // Handle edge condition
        if (this.maxScore === score) {
            return (this.bins.length - 1);
        }

        value = (score / this.maxScore);
        value *= this.bins.length;

        return Math.floor(value);
    };

    // Render
    cursor.CursorHistogram.prototype.render = function (track) {

        var myself = this;
        var renderMinimumOverlay = function (minimum) {

            var height = (minimum/track.max) * myself.bins.length;
            myself.igvCanvas.fillRect(0, myself.bins.length - height, myself.canvasWidth, height, { fillStyle: myself.minMaxfillStyle });

            myself.igvCanvas.fillRect(0, myself.bins.length - height - 1, myself.canvasWidth, 1, { fillStyle: myself.minMaxEdgefillStyle });

        };

        var renderMaximumOverlay = function (maximum) {

            var height = myself.bins.length - ((maximum/track.max) * myself.bins.length);
            myself.igvCanvas.fillRect(0, 0, myself.canvasWidth, height, { fillStyle: myself.minMaxfillStyle });

            myself.igvCanvas.fillRect(0, height - 1, myself.canvasWidth, 1, { fillStyle: myself.minMaxEdgefillStyle });

        };

        // Clear canvas
        this.fillCanvasWithFillStyle(this.canvasFillStyle);

        // render histogram
        this.bins.forEach(function (count, index, counts) {

            var x,
                y,
                width,
                height,
                percent,
                color;

            if (count) {

                percent = (count/this.maxCount);

                // Symmetric centerline histogram. Pretty.
                x = ((1.0 - percent) / 2.0) * this.canvasWidth;

                // Asymmetric histogram. Meh.
//            x = (1.0 - percent) * this.canvasWidth;

                width = (percent) * this.canvasWidth;

                y = (counts.length - 1) - index;
                height = 1;

                color = (track.color) ? track.color : igv.rgbColor(128, 128, 128);

                this.igvCanvas.fillRect(x, y, width, height, { fillStyle: color });
            }

        }, this);

        var renderTrackFilterOverlays = track.trackFilter.makeTrackFilterOverlayRenderer(renderMinimumOverlay, renderMaximumOverlay);
        renderTrackFilterOverlays();

    };

    cursor.CursorHistogram.prototype.fillCanvasWithFillStyle = function (fillStyle) {
        this.igvCanvas.fillRect(0, 0, this.canvasWidth, this.canvasHeight, { fillStyle:fillStyle } );
    };

    function showX(count, index, counts) {

        var yPercent = index/(counts.length - 1),
            color = igv.rgbaColor(Math.floor(yPercent * 255), 0, 0, 0.75);

        this.igvCanvas.fillRect(index, 0, 1, counts.length, { fillStyle: color });

    }

    function showY(count, index, counts) {

        var yPercent = index/(counts.length - 1),
            color = igv.rgbaColor(Math.floor(yPercent * 255), 0, 0, 0.75);

        this.igvCanvas.fillRect(0, index, counts.length, 1, { fillStyle: color });

    }

    // Markup
    cursor.CursorHistogram.prototype.createMarkupWithTrackPanelDiv = function (trackPanel) {

//        this.id = this.guid +"_cursorHistogramDiv";
//        this.label = trackPanel.track.label +"_cursorHistogramDiv";

        this.igvCanvas = this.canvasWithParentDiv(trackPanel.controlDiv);

        // Clear canvas
        this.fillCanvasWithFillStyle(this.canvasFillStyle);

    };

    cursor.CursorHistogram.prototype.canvasWithParentDiv = function (parentDiv) {

        var cursorHistogramDiv = document.createElement('div');
        parentDiv.appendChild(cursorHistogramDiv);

        cursorHistogramDiv.className = "igv-cursorHistogram-div";
        cursorHistogramDiv.style.height = this.bins.length + "px";
        this.cursorHistogramDiv = cursorHistogramDiv;

        var DOMCanvas = this.DOMCanvasWithParentDiv(this.cursorHistogramDiv);

        var igvCanvas = new igv.Canvas(DOMCanvas);
        return igvCanvas;
    };

    cursor.CursorHistogram.prototype.DOMCanvasWithParentDiv = function (parentDiv) {

        var DOMCanvas;

        DOMCanvas = document.createElement('canvas');
        parentDiv.appendChild(DOMCanvas);

        this.canvasWidth = parentDiv.clientWidth;
        this.canvasHeight = parentDiv.clientHeight;

        DOMCanvas.setAttribute('width', parentDiv.clientWidth);
        DOMCanvas.setAttribute('height', parentDiv.clientHeight);

        return DOMCanvas;
    };

    cursor.CursorHistogram.prototype.updateHeight = function (track, height) {

        this.cursorHistogramDiv.style.height = height + "px";
        this.canvasHeight = this.cursorHistogramDiv.clientHeight;
        this.igvCanvas.canvas.setAttribute('height', this.cursorHistogramDiv.clientHeight);

        this.bins = [];
        this.bins.length = this.canvasHeight;
        track.cursorModel.initializeHistogram(track);
     };

    return cursor;

})(cursor || {});

<commit_msg>CURSOR. Removed dark edge from cursor histogram min/max overlay scrim<commit_after>/**
 * Created by turner on 6/19/14.
 */
var cursor = (function (cursor) {

    cursor.CursorHistogram = function (binCount, maxScore) {

//        this.guid = igv.guid();
        this.bins = [];
        this.bins.length = binCount;
        this.maxCount = 0;

        this.canvasFillStyle = igv.greyScale(255);
//        this.canvasFillStyle = igv.rgbColor(255, 255, 255);
        this.minMaxfillStyle = igv.rgbaColor(64, 64, 64, 0.5);
        this.minMaxEdgefillStyle = igv.rgbaColor(32, 32, 32, 1.0);

        this.initializeBins();

        this.maxScore = maxScore;

    };

    // Methods
    cursor.CursorHistogram.prototype.initializeBins = function () {

        var i, len;
        for (i=0, len=this.bins.length; i < len; i++) {
            this.bins[i] = 0;
        }

        this.maxCount = 0;
    };

    cursor.CursorHistogram.prototype.insertScore = function (score) {

        if (score < 0) {
            return;
        }

        var index = this.scoreIndex(score);
        this.bins[ index ] += 1;
        this.maxCount = Math.max(this.maxCount, this.bins[ index ]);
    };

    cursor.CursorHistogram.prototype.scoreIndex = function (score) {

        var value;

        // Handle edge condition
        if (this.maxScore === score) {
            return (this.bins.length - 1);
        }

        value = (score / this.maxScore);
        value *= this.bins.length;

        return Math.floor(value);
    };

    // Render
    cursor.CursorHistogram.prototype.render = function (track) {

        var myself = this;
        var renderMinimumOverlay = function (minimum) {

            var height = (minimum/track.max) * myself.bins.length;
            myself.igvCanvas.fillRect(0, myself.bins.length - height, myself.canvasWidth, height, { fillStyle: myself.minMaxfillStyle });

//            myself.igvCanvas.fillRect(0, myself.bins.length - height - 1, myself.canvasWidth, 1, { fillStyle: myself.minMaxEdgefillStyle });

        };

        var renderMaximumOverlay = function (maximum) {

            var height = myself.bins.length - ((maximum/track.max) * myself.bins.length);
            myself.igvCanvas.fillRect(0, 0, myself.canvasWidth, height, { fillStyle: myself.minMaxfillStyle });

//            myself.igvCanvas.fillRect(0, height - 1, myself.canvasWidth, 1, { fillStyle: myself.minMaxEdgefillStyle });

        };

        // Clear canvas
        this.fillCanvasWithFillStyle(this.canvasFillStyle);

        // render histogram
        this.bins.forEach(function (count, index, counts) {

            var x,
                y,
                width,
                height,
                percent,
                color;

            if (count) {

                percent = (count/this.maxCount);

                // Symmetric centerline histogram. Pretty.
                x = ((1.0 - percent) / 2.0) * this.canvasWidth;

                // Asymmetric histogram. Meh.
//            x = (1.0 - percent) * this.canvasWidth;

                width = (percent) * this.canvasWidth;

                y = (counts.length - 1) - index;
                height = 1;

                color = (track.color) ? track.color : igv.rgbColor(128, 128, 128);

                this.igvCanvas.fillRect(x, y, width, height, { fillStyle: color });
            }

        }, this);

        var renderTrackFilterOverlays = track.trackFilter.makeTrackFilterOverlayRenderer(renderMinimumOverlay, renderMaximumOverlay);
        renderTrackFilterOverlays();

    };

    cursor.CursorHistogram.prototype.fillCanvasWithFillStyle = function (fillStyle) {
        this.igvCanvas.fillRect(0, 0, this.canvasWidth, this.canvasHeight, { fillStyle:fillStyle } );
    };

    function showX(count, index, counts) {

        var yPercent = index/(counts.length - 1),
            color = igv.rgbaColor(Math.floor(yPercent * 255), 0, 0, 0.75);

        this.igvCanvas.fillRect(index, 0, 1, counts.length, { fillStyle: color });

    }

    function showY(count, index, counts) {

        var yPercent = index/(counts.length - 1),
            color = igv.rgbaColor(Math.floor(yPercent * 255), 0, 0, 0.75);

        this.igvCanvas.fillRect(0, index, counts.length, 1, { fillStyle: color });

    }

    // Markup
    cursor.CursorHistogram.prototype.createMarkupWithTrackPanelDiv = function (trackPanel) {

//        this.id = this.guid +"_cursorHistogramDiv";
//        this.label = trackPanel.track.label +"_cursorHistogramDiv";

        this.igvCanvas = this.canvasWithParentDiv(trackPanel.controlDiv);

        // Clear canvas
        this.fillCanvasWithFillStyle(this.canvasFillStyle);

    };

    cursor.CursorHistogram.prototype.canvasWithParentDiv = function (parentDiv) {

        var cursorHistogramDiv = document.createElement('div');
        parentDiv.appendChild(cursorHistogramDiv);

        cursorHistogramDiv.className = "igv-cursorHistogram-div";
        cursorHistogramDiv.style.height = this.bins.length + "px";
        this.cursorHistogramDiv = cursorHistogramDiv;

        var DOMCanvas = this.DOMCanvasWithParentDiv(this.cursorHistogramDiv);

        var igvCanvas = new igv.Canvas(DOMCanvas);
        return igvCanvas;
    };

    cursor.CursorHistogram.prototype.DOMCanvasWithParentDiv = function (parentDiv) {

        var DOMCanvas;

        DOMCanvas = document.createElement('canvas');
        parentDiv.appendChild(DOMCanvas);

        this.canvasWidth = parentDiv.clientWidth;
        this.canvasHeight = parentDiv.clientHeight;

        DOMCanvas.setAttribute('width', parentDiv.clientWidth);
        DOMCanvas.setAttribute('height', parentDiv.clientHeight);

        return DOMCanvas;
    };

    cursor.CursorHistogram.prototype.updateHeight = function (track, height) {

        this.cursorHistogramDiv.style.height = height + "px";
        this.canvasHeight = this.cursorHistogramDiv.clientHeight;
        this.igvCanvas.canvas.setAttribute('height', this.cursorHistogramDiv.clientHeight);

        this.bins = [];
        this.bins.length = this.canvasHeight;
        track.cursorModel.initializeHistogram(track);
     };

    return cursor;

})(cursor || {});

<|endoftext|>


<commit_before>/**
    For conditions of distribution and use, see copyright notice in LICENSE

    MenuBar.js - Implements basic menu bar for accessing common Tundra functionality and UIs.
    NOTE: Clears any existing menus in the menu bar so make sure that this script
    is loaded first, or replace with another implementation*/

// Widget for showing instructions if user starts Tundra without any scene.
var sceneInstructions = null;

// Applicable only in headful mode.
if (!framework.IsHeadless())
{
    engine.ImportExtension("qt.core");
    engine.ImportExtension("qt.gui");

    // Use absolute paths for rel image files so they always work!
    var installDir = QDir.fromNativeSeparators(application.installationDirectory);

    // Get the menubar
    var menu = ui.MainWindow().menuBar();
    menu.clear();

    // File menu
    var fileMenu = menu.addMenu("&File");
    fileMenu.addAction("New scene").triggered.connect(NewScene);
    fileMenu.addAction("Open scene").triggered.connect(OpenScene);
    fileMenu.addSeparator();

    var screenshotAct = fileMenu.addAction("Take Screenshot");
    screenshotAct.triggered.connect(TakeScreenshot);
    screenshotAct.enabled = false;

    fileMenu.addSeparator();
    fileMenu.addAction("Clear Asset Cache").triggered.connect(ClearAssetCache);
    fileMenu.addSeparator();

    // Reconnect menu items for client only
    if (!server.IsAboutToStart())
    {
        var disconnectAction = fileMenu.addAction(new QIcon(installDir + "data/ui/images/icon/disconnect.ico"), "Disconnect");
        disconnectAction.triggered.connect(Disconnect);
        client.Connected.connect(Connected);
        client.Disconnected.connect(Disconnected);
        disconnectAction.enabled = false;
    }
    fileMenu.addAction(new QIcon(installDir + "data/ui/images/icon/system-shutdown.ico"), "Quit").triggered.connect(Quit);

    // View menu
    var viewMenu = menu.addMenu("&View");
    // Set unique object name so that other scripts can query this menu.
    viewMenu.objectName = "ViewMenu";

    if (framework.ModuleByName("SceneStructure"))
    {
        viewMenu.addAction("Assets").triggered.connect(OpenAssetsWindow);
        viewMenu.addAction("Scene").triggered.connect(OpenSceneWindow);
        viewMenu.addAction("Key Bindings").triggered.connect(OpenKeyBindingsWindow);
    }

    var ecEditor = framework.ModuleByName("ECEditor");
    if (ecEditor)
        viewMenu.addAction("EC Editor").triggered.connect(OpenEcEditorWindow);

    // TODO: Avatar Editor menu action disabled for now, as it's not fully ready for end-users
//    if (framework.ModuleByName("Avatar"))

    if (framework.ModuleByName("DebugStats"))
        viewMenu.addAction("Profiler").triggered.connect(OpenProfilerWindow);

    if (framework.ModuleByName("PythonScript"))
        viewMenu.addAction("Python Console").triggered.connect(OpenPythonConsole);

    if (console)
        toolsMenu.addAction("Show Console").triggered.connect(OpenConsoleWindow);

    // Settings menu
    if (framework.ModuleByName("MumbleVoip") || framework.ModuleByName("CAVEStereo") || ecEditor)
    {
        var settingsMenu = menu.addMenu("&Settings");
        // Set unique object name so that other scripts can query this menu.
        settingsMenu.objectName = "SettingsMenu";

        settingsMenu.addAction("Open config folder").triggered.connect(OpenConfigFolder);

        if (framework.ModuleByName("MumbleVoip"))
            settingsMenu.addAction("Voice settings").triggered.connect(OpenVoiceSettings);

        if (ecEditor)
        {
            // Gizmo
            var showGizmoAction = settingsMenu.addAction("Show editing gizmo");
            showGizmoAction.checkable = true;
            showGizmoAction.checked = ecEditor.gizmoEnabled;
            showGizmoAction.triggered.connect(ShowEditingGizmo);

            // Highlighting of selected entities
            var showHighlightAction = settingsMenu.addAction("Highlight selected entities");
            showHighlightAction.checkable = true;
            showHighlightAction.checked = ecEditor.highlightingEnabled;
            showHighlightAction.triggered.connect(HighlightSelectedEntities);
        }
    }

    // Help menu
    var helpMenu = menu.addMenu("&Help");
    var browserIcon = new QIcon(installDir + "data/ui/images/icon/browser.ico");
    helpMenu.addAction(browserIcon, "Wiki").triggered.connect(OpenWikiUrl);
    helpMenu.addAction(browserIcon, "Doxygen").triggered.connect(OpenDoxygenUrl);
    helpMenu.addAction(browserIcon, "Mailing list").triggered.connect(OpenMailingListUrl);

    // If we started without scene, show instructions for the user.
    if (!framework.Scene().MainCameraScene())
    {
        framework.Scene().SceneAdded.connect(HideSceneInstructions);
        ShowSceneInstructions();
    }

    function ShowSceneInstructions()
    {
        var label = new QLabel();
        label.indent = 10;
        label.text = "Tundra has started succesfully. You have no active scene currently.\n" +
            "Startup scenes can be specified by using the --file command line parameter. Run Tundra --help for instructions. \n" +
            "Use File -> Open scene to load an existing scene or New Scene to create an empty scene.";
        label.resize(800, 200);
        label.setStyleSheet("QLabel {color: white; background-color: transparent; font-size: 16px; }");

        sceneInstructions = new UiProxyWidget(label);
        ui.AddProxyWidgetToScene(sceneInstructions);
        sceneInstructions.x = 50
        sceneInstructions.y = 50;
        sceneInstructions.windowFlags = 0;
        sceneInstructions.visible = true;
        sceneInstructions.focusPolicy = Qt.NoFocus;
    }

    function HideSceneInstructions()
    {
        if (sceneInstructions)
        {
            sceneInstructions.deleteLater();
            sceneInstructions = null;
        }
    }

    function NewScene() {
        if (framework.Scene().MainCameraScene() != null)
        {
            var result = QMessageBox.warning(ui.MainWindow(), "New scene", "Making a new scene will discard any changes you made to the current scene. Do you want to continue?", QMessageBox.Yes, QMessageBox.No);
            if (result == QMessageBox.No)
                return;
        }
        var sceneNumber = Math.floor(Math.random() * 10000000 + 1);
        var newScene = framework.Scene().CreateScene("Scene" + sceneNumber, true, true);
        if (newScene == null)
            return;

        // Give some sort of feedback (f.ex. make a skybox)
        var environment = newScene.CreateEntity(newScene.NextFreeId(), ["EC_Name", "EC_Sky"]);
        environment.name = "Environment";
        environment.sky.enabled = true;
    }

    function OpenScene() {
        var fileName = QFileDialog.getOpenFileName(ui.MainWindow(), "Open scene", framework.application.currentWorkingDirectory, "Tundra TXML file (*.txml)");
        if (fileName == "")
            return;

        if (framework.Scene().MainCameraScene() != null)
        {
            var result = QMessageBox.warning(ui.MainWindow(), "Open scene", "Opening a new scene will discard any changes you made to the current scene. Do you want to continue?", QMessageBox.Yes, QMessageBox.No);
            if (result == QMessageBox.No)
                return;
        }

        var sceneNumber = Math.floor(Math.random() * 10000000 + 1);
        var openedScene = framework.Scene().CreateScene("Scene" + sceneNumber, true, true);
        if (openedScene == null)
            return;
        var openedStorage = asset.DeserializeAssetStorageFromString(fileName, false);
        if (openedStorage != null)
            asset.SetDefaultAssetStorage(openedStorage);

        openedScene.LoadSceneXML(fileName, true, false, 0);

    }

    function Reconnect() {
        client.Reconnect();
    }

    function Disconnect() {
        client.Logout();
    }

    function Connected() {
        disconnectAction.enabled = true;
        screenshotAct.enabled = true;
    }

    function Disconnected() {
        disconnectAction.enabled = false;
        screenshotAct.enabled = false;
    }

    function Quit() {
        framework.Exit();
    }

    function TakeScreenshot() {
        var mainCamera = renderer.MainCameraComponent();
        var imgPath = mainCamera.SaveScreenshot();
        QDesktopServices.openUrl(new QUrl(imgPath));
    }

    function ClearAssetCache() {
        // Show some additional info: count and size of removed files
        var cachePath = asset.Cache().CacheDirectory();
        if (cachePath != "" && cachePath != null)
        {
            var count = 0;
            var size = 0;

            var cacheDir = new QDir(cachePath);
            var cacheFiles = cacheDir.entryInfoList();  // Using flags here would be nice but does not seem to work
            for(var i=0; i<cacheFiles.length; ++i)
            {
                try
                {
                    if (cacheFiles[i].isFile() && cacheFiles[i].exists())
                    {
                        count++;
                        size += cacheFiles[i].size();
                    }
                }
                catch(e)
                {
                    console.LogError("Failed to iterate files in cache: " + e);
                    count = null;
                    size = null;
                    break;
                }
            }

            // Clear the cache. If we happen to throw here we wont show false log/ui information.
            asset.Cache().ClearAssetCache();

            // Log to console
            var msg = "";
            if (count != null && size != null)
            {
                // Round to two decimals
                var roundedSize = Math.round(((size/1024)/1024) * Math.pow(10,2)) / Math.pow(10,2);
                msg = "Cleared " + count + " files with total size of " + roundedSize + " mb from asset cache."
            }
            else
                msg = "Errors occurred while calculating cache file information. Cache cleared.";
            console.LogInfo(msg);

            // Show information box for user. Note: this will block so not the most convenient.
            if (ui.MainWindow() != null)
                QMessageBox.information(ui.MainWindow(), "Cache Cleared", msg);
        }
    }

    function OpenMailingListUrl() {
        QDesktopServices.openUrl(new QUrl("http://groups.google.com/group/realxtend/"));
    }

    function OpenWikiUrl() {
        QDesktopServices.openUrl(new QUrl("http://wiki.realxtend.org/"));
    }

    function OpenDoxygenUrl() {
        QDesktopServices.openUrl(new QUrl("http://www.realxtend.org/doxygen/"));
    }

    function OpenSceneWindow() {
        framework.ModuleByName("SceneStructure").ToggleSceneStructureWindow();
    }

    function OpenAssetsWindow() {
        framework.ModuleByName("SceneStructure").ToggleAssetsWindow();
    }

    function OpenKeyBindingsWindow() {
        framework.ModuleByName("SceneStructure").ToggleKeyBindingsWindow();
    }

    function OpenProfilerWindow() {
        framework.ModuleByName("DebugStats").ShowProfilerWindow();
    }

    function OpenVoiceSettings() {
        framework.ModuleByName("MumbleVoip").ToggleSettingsWidget();
    }

    function OpenConsoleWindow() {
        framework.GetModuleByName("Console").ToggleConsole();
    }

    function OpenEcEditorWindow() {
        framework.ModuleByName("ECEditor").ShowEditorWindow();
    }

    function OpenAvatarEditorWindow() {
        framework.ModuleByName("Avatar").ToggleAvatarEditorWindow();
        if (client.IsConnected())
           framework.ModuleByName("Avatar").EditAvatar("Avatar" + client.connectionId)
   }

    function ShowEditingGizmo(show) {
        framework.ModuleByName("ECEditor").gizmoEnabled = show;
    }

    function HighlightSelectedEntities(show) {
        framework.ModuleByName("ECEditor").highlightingEnabled = show;
    }

    function OpenConfigFolder() {
        QDesktopServices.openUrl(new QUrl(config.ConfigFolder()));
    }
}
<commit_msg>Clean up Cave and Python items from MenuBar.js. Closes #684.<commit_after>/**
    For conditions of distribution and use, see copyright notice in LICENSE

    MenuBar.js - Implements basic menu bar for accessing common Tundra functionality and UIs.
    NOTE: Clears any existing menus in the menu bar so make sure that this script
    is loaded first, or replace with another implementation*/

// Widget for showing instructions if user starts Tundra without any scene.
var sceneInstructions = null;

// Applicable only in headful mode.
if (!framework.IsHeadless())
{
    engine.ImportExtension("qt.core");
    engine.ImportExtension("qt.gui");

    // Use absolute paths for rel image files so they always work!
    var installDir = QDir.fromNativeSeparators(application.installationDirectory);

    // Get the menubar
    var menu = ui.MainWindow().menuBar();
    menu.clear();

    // File menu
    var fileMenu = menu.addMenu("&File");
    fileMenu.addAction("New scene").triggered.connect(NewScene);
    fileMenu.addAction("Open scene").triggered.connect(OpenScene);
    fileMenu.addSeparator();

    var screenshotAct = fileMenu.addAction("Take Screenshot");
    screenshotAct.triggered.connect(TakeScreenshot);
    screenshotAct.enabled = false;

    fileMenu.addSeparator();
    fileMenu.addAction("Clear Asset Cache").triggered.connect(ClearAssetCache);
    fileMenu.addSeparator();

    // Reconnect menu items for client only
    if (!server.IsAboutToStart())
    {
        var disconnectAction = fileMenu.addAction(new QIcon(installDir + "data/ui/images/icon/disconnect.ico"), "Disconnect");
        disconnectAction.triggered.connect(Disconnect);
        client.Connected.connect(Connected);
        client.Disconnected.connect(Disconnected);
        disconnectAction.enabled = false;
    }
    fileMenu.addAction(new QIcon(installDir + "data/ui/images/icon/system-shutdown.ico"), "Quit").triggered.connect(Quit);

    // View menu
    var viewMenu = menu.addMenu("&View");
    // Set unique object name so that other scripts can query this menu.
    viewMenu.objectName = "ViewMenu";

    if (framework.ModuleByName("SceneStructure"))
    {
        viewMenu.addAction("Assets").triggered.connect(OpenAssetsWindow);
        viewMenu.addAction("Scene").triggered.connect(OpenSceneWindow);
        viewMenu.addAction("Key Bindings").triggered.connect(OpenKeyBindingsWindow);
    }

    var ecEditor = framework.ModuleByName("ECEditor");
    if (ecEditor)
        viewMenu.addAction("EC Editor").triggered.connect(OpenEcEditorWindow);

    // TODO: Avatar Editor menu action disabled for now, as it's not fully ready for end-users
//    if (framework.ModuleByName("Avatar"))

    if (framework.ModuleByName("DebugStats"))
        viewMenu.addAction("Profiler").triggered.connect(OpenProfilerWindow);

    toolsMenu.addAction("Show Console").triggered.connect(OpenConsoleWindow);

    // Settings menu
    if (framework.ModuleByName("MumbleVoip") || ecEditor)
    {
        var settingsMenu = menu.addMenu("&Settings");
        // Set unique object name so that other scripts can query this menu.
        settingsMenu.objectName = "SettingsMenu";

        settingsMenu.addAction("Open config folder").triggered.connect(OpenConfigFolder);

        if (framework.ModuleByName("MumbleVoip"))
            settingsMenu.addAction("Voice settings").triggered.connect(OpenVoiceSettings);

        if (ecEditor)
        {
            // Gizmo
            var showGizmoAction = settingsMenu.addAction("Show editing gizmo");
            showGizmoAction.checkable = true;
            showGizmoAction.checked = ecEditor.gizmoEnabled;
            showGizmoAction.triggered.connect(ShowEditingGizmo);

            // Highlighting of selected entities
            var showHighlightAction = settingsMenu.addAction("Highlight selected entities");
            showHighlightAction.checkable = true;
            showHighlightAction.checked = ecEditor.highlightingEnabled;
            showHighlightAction.triggered.connect(HighlightSelectedEntities);
        }
    }

    // Help menu
    var helpMenu = menu.addMenu("&Help");
    var browserIcon = new QIcon(installDir + "data/ui/images/icon/browser.ico");
    helpMenu.addAction(browserIcon, "Wiki").triggered.connect(OpenWikiUrl);
    helpMenu.addAction(browserIcon, "Doxygen").triggered.connect(OpenDoxygenUrl);
    helpMenu.addAction(browserIcon, "Mailing list").triggered.connect(OpenMailingListUrl);

    // If we started without scene, show instructions for the user.
    if (!framework.Scene().MainCameraScene())
    {
        framework.Scene().SceneAdded.connect(HideSceneInstructions);
        ShowSceneInstructions();
    }

    function ShowSceneInstructions()
    {
        var label = new QLabel();
        label.indent = 10;
        label.text = "Tundra has started succesfully. You have no active scene currently.\n" +
            "Startup scenes can be specified by using the --file command line parameter. Run Tundra --help for instructions. \n" +
            "Use File -> Open scene to load an existing scene or New Scene to create an empty scene.";
        label.resize(800, 200);
        label.setStyleSheet("QLabel {color: white; background-color: transparent; font-size: 16px; }");

        sceneInstructions = new UiProxyWidget(label);
        ui.AddProxyWidgetToScene(sceneInstructions);
        sceneInstructions.x = 50
        sceneInstructions.y = 50;
        sceneInstructions.windowFlags = 0;
        sceneInstructions.visible = true;
        sceneInstructions.focusPolicy = Qt.NoFocus;
    }

    function HideSceneInstructions()
    {
        if (sceneInstructions)
        {
            sceneInstructions.deleteLater();
            sceneInstructions = null;
        }
    }

    function NewScene() {
        if (framework.Scene().MainCameraScene() != null)
        {
            var result = QMessageBox.warning(ui.MainWindow(), "New scene", "Making a new scene will discard any changes you made to the current scene. Do you want to continue?", QMessageBox.Yes, QMessageBox.No);
            if (result == QMessageBox.No)
                return;
        }
        var sceneNumber = Math.floor(Math.random() * 10000000 + 1);
        var newScene = framework.Scene().CreateScene("Scene" + sceneNumber, true, true);
        if (newScene == null)
            return;

        // Give some sort of feedback (f.ex. make a skybox)
        var environment = newScene.CreateEntity(newScene.NextFreeId(), ["EC_Name", "EC_Sky"]);
        environment.name = "Environment";
        environment.sky.enabled = true;
    }

    function OpenScene() {
        var fileName = QFileDialog.getOpenFileName(ui.MainWindow(), "Open scene", framework.application.currentWorkingDirectory, "Tundra TXML file (*.txml)");
        if (fileName == "")
            return;

        if (framework.Scene().MainCameraScene() != null)
        {
            var result = QMessageBox.warning(ui.MainWindow(), "Open scene", "Opening a new scene will discard any changes you made to the current scene. Do you want to continue?", QMessageBox.Yes, QMessageBox.No);
            if (result == QMessageBox.No)
                return;
        }

        var sceneNumber = Math.floor(Math.random() * 10000000 + 1);
        var openedScene = framework.Scene().CreateScene("Scene" + sceneNumber, true, true);
        if (openedScene == null)
            return;
        var openedStorage = asset.DeserializeAssetStorageFromString(fileName, false);
        if (openedStorage != null)
            asset.SetDefaultAssetStorage(openedStorage);

        openedScene.LoadSceneXML(fileName, true, false, 0);

    }

    function Reconnect() {
        client.Reconnect();
    }

    function Disconnect() {
        client.Logout();
    }

    function Connected() {
        disconnectAction.enabled = true;
        screenshotAct.enabled = true;
    }

    function Disconnected() {
        disconnectAction.enabled = false;
        screenshotAct.enabled = false;
    }

    function Quit() {
        framework.Exit();
    }

    function TakeScreenshot() {
        var mainCamera = renderer.MainCameraComponent();
        var imgPath = mainCamera.SaveScreenshot();
        QDesktopServices.openUrl(new QUrl(imgPath));
    }

    function ClearAssetCache() {
        // Show some additional info: count and size of removed files
        var cachePath = asset.Cache().CacheDirectory();
        if (cachePath != "" && cachePath != null)
        {
            var count = 0;
            var size = 0;

            var cacheDir = new QDir(cachePath);
            var cacheFiles = cacheDir.entryInfoList();  // Using flags here would be nice but does not seem to work
            for(var i=0; i<cacheFiles.length; ++i)
            {
                try
                {
                    if (cacheFiles[i].isFile() && cacheFiles[i].exists())
                    {
                        count++;
                        size += cacheFiles[i].size();
                    }
                }
                catch(e)
                {
                    console.LogError("Failed to iterate files in cache: " + e);
                    count = null;
                    size = null;
                    break;
                }
            }

            // Clear the cache. If we happen to throw here we wont show false log/ui information.
            asset.Cache().ClearAssetCache();

            // Log to console
            var msg = "";
            if (count != null && size != null)
            {
                // Round to two decimals
                var roundedSize = Math.round(((size/1024)/1024) * Math.pow(10,2)) / Math.pow(10,2);
                msg = "Cleared " + count + " files with total size of " + roundedSize + " mb from asset cache."
            }
            else
                msg = "Errors occurred while calculating cache file information. Cache cleared.";
            console.LogInfo(msg);

            // Show information box for user. Note: this will block so not the most convenient.
            if (ui.MainWindow() != null)
                QMessageBox.information(ui.MainWindow(), "Cache Cleared", msg);
        }
    }

    function OpenMailingListUrl() {
        QDesktopServices.openUrl(new QUrl("http://groups.google.com/group/realxtend/"));
    }

    function OpenWikiUrl() {
        QDesktopServices.openUrl(new QUrl("http://wiki.realxtend.org/"));
    }

    function OpenDoxygenUrl() {
        QDesktopServices.openUrl(new QUrl("http://www.realxtend.org/doxygen/"));
    }

    function OpenSceneWindow() {
        framework.ModuleByName("SceneStructure").ToggleSceneStructureWindow();
    }

    function OpenAssetsWindow() {
        framework.ModuleByName("SceneStructure").ToggleAssetsWindow();
    }

    function OpenKeyBindingsWindow() {
        framework.ModuleByName("SceneStructure").ToggleKeyBindingsWindow();
    }

    function OpenProfilerWindow() {
        framework.ModuleByName("DebugStats").ShowProfilerWindow();
    }

    function OpenVoiceSettings() {
        framework.ModuleByName("MumbleVoip").ToggleSettingsWidget();
    }

    function OpenConsoleWindow() {
        framework.GetModuleByName("Console").ToggleConsole();
    }

    function OpenEcEditorWindow() {
        framework.ModuleByName("ECEditor").ShowEditorWindow();
    }

    function OpenAvatarEditorWindow() {
        framework.ModuleByName("Avatar").ToggleAvatarEditorWindow();
        if (client.IsConnected())
           framework.ModuleByName("Avatar").EditAvatar("Avatar" + client.connectionId)
   }

    function ShowEditingGizmo(show) {
        framework.ModuleByName("ECEditor").gizmoEnabled = show;
    }

    function HighlightSelectedEntities(show) {
        framework.ModuleByName("ECEditor").highlightingEnabled = show;
    }

    function OpenConfigFolder() {
        QDesktopServices.openUrl(new QUrl(config.ConfigFolder()));
    }
}
<|endoftext|>


<commit_before>/*Group.js
 * path object
 * extends Duplicar node (for the time being)
 * node with actual path in it
 */

define([
  'underscore',
  'paper',
  'models/data/geometry/GeometryNode',
  'utils/TrigFunc',
  'models/data/properties/PFloat',
  'models/data/properties/PPoint',

], function(_, paper, GeometryNode, TrigFunc, PFloat, PPoint) {

  var Group = GeometryNode.extend({

    defaults: _.extend({}, GeometryNode.prototype.defaults, {

      name: 'group',
      type: 'geometry',
      points: null,
      open: false,

    }),

    initialize: function(attributes, options) {
      GeometryNode.prototype.initialize.apply(this, arguments);
      this.geometryGenerator = options.geometryGenerator;
      this.get('scalingDelta').setValue({
        x: 1,
        y: 1,
        operator: 'set'
      });
      var geom = new paper.Group();
      geom.applyMatrix = false;
      this.set('geom', geom);
      geom.data.instance = this;
      geom.data.geom = true;
      geom.data.nodetype = this.get('name');
      this.get('fillColor').setNoColor(true);
      this.get('strokeColor').setNoColor(true);
      this.get('strokeWidth').setValue(1);
      var ui_group = new paper.Group();
      var targetLayer = paper.project.layers.filter(function(layer) {
        return layer.name === 'ui_layer';
      })[0];
      targetLayer.addChild(ui_group);
      this.set('bbox', ui_group);
      this.createBBox();
      this.currentBounds = new paper.Rectangle(0, 0, 0, 0);
      /*
      this.startingUI = new paper.Path.Circle(new paper.Point(0,0),10);
      this.startingUI.fillColor = 'green';
      this.endingUI = this.startingUI.clone();
      this.endingUI.fillColor = 'red';*/

      this.bboxInvalid = false;
      this.childrenModified = false;
    },

    toJSON: function(noUndoCache) {
      var data = GeometryNode.prototype.toJSON.call(this, noUndoCache);
      data.matrix = this.get('geom').matrix.values;
      data.childrenModified = this.childrenModified;

      for (var i = 0; i < this.children.length; i++) {
        data.children.push(this.children[i].toJSON(noUndoCache));
      }
      return data;
    },

    clearUndoCache: function() {
      for (var i = 0; i < this.children.length; i++) {
        this.children[i].clearUndoCache();
      }
      GeometryNode.prototype.clearUndoCache.call(this);
    },

    parseJSON: function(data, manager) {
      var changed = GeometryNode.prototype.parseJSON.call(this, data, manager);
      var childClone = this.children.slice(0, this.children.length);
      var dataClone = data.children.slice(0, data.children.length);

      for (var i = 0; i < this.children.length; i++) {
        var target_id = this.children[i].get('id');
        var target_data = _.find(data.children, function(item) {
          return item.id == target_id;
        });
        //if the child currently exists in the group
        if (target_data) {
          var mI = this.children[i].parseJSON(target_data);
          changed.toRemove.push.apply(changed, mI.toRemove);
          changed.toAdd.push.apply(changed, mI.toAdd);
          childClone = _.filter(childClone, function(child) {
            return child.get('id') != target_id;
          });
          dataClone = _.filter(dataClone, function(data) {
            return data.id != target_id;
          });
        }
      }

      //remove children not in JSON
      for (var j = 0; j < childClone.length; j++) {
        var currentFuture = this.futureStates[this.futureStates.length - 1];
        var currentPast = this.previousStates[this.previousStates.length - 1];
        if (currentFuture) {
          var targetFuture = _.find(currentFuture.children, function(item) {
            return item.id == childClone[j].get('id');
          });
          if (targetFuture) {
            targetFuture.futureStates = childClone[j].futureStates;
            targetFuture.previousStates = childClone[j].previousStates;
          }
        }
        if (currentPast) {
          var targetPast = _.find(currentPast.children, function(item) {
            return item.id == childClone[j].get('id');
          });
          if (targetPast) {
            targetPast.futureStates = childClone[j].futureStates;
            targetPast.previousStates = childClone[j].previousStates;
          }
        }

        var removed = this.removeChildNode(childClone[j]);
        changed.toRemove.push(removed);
        removed.deleteSelf();
      }

      //addChildren in JSON that didn't already exist
      for (var k = 0; k < dataClone.length; k++) {
        var newChild = this.geometryGenerator.getTargetClass(dataClone[k].name);
        //TODO: not sure why this is needed..
        if (newChild.get('name') == 'duplicator') {
          newChild.stopListening(newChild.masterList);
          newChild.stopListening(newChild.internalList);
        }
        changed.toAdd.push(newChild);
        newChild.parseJSON(dataClone[k]);
        newChild.previousStates = dataClone[k].previousStates;
        newChild.futureStates = dataClone[k].futureStates;
        this.insertChild(dataClone[k].zIndex, newChild);
        if (data.matrix) {
          newChild.childrenModified = true;
          var matrix = new paper.Matrix(data.matrix[0], data.matrix[1], data.matrix[2], data.matrix[3], data.matrix[4], data.matrix[5]);
          newChild.get('geom').transform(matrix);
          newChild.set('rendered', false);
          newChild.reset();
          newChild.render();
        }
      }

      return changed;

    },

    parseInheritorJSON: function(data, manager) {
      for (var i = 0; i < this.children.length; i++) {
        var c_data = data.children[i];
        var child = this.children[i];
        child.parseInheritorJSON(c_data, manager);
      }
    },

    getById: function(id) {
      if (this.get('id') == id) {
        return this;
      } else {
        for (var i = 0; i < this.children.length; i++) {
          var match = this.children[i].getById(id);
          if (match) {
            return match;
          }
        }
      }
    },

    getInternalList: function(id) {
      var list;
      for (var i = 0; i < this.children.length; i++) {
        list = this.children[i].getInternalList(id);
        if (list) {
          return list;
        }
      }
    },

    getInternalListOwner: function(id) {
      var owner;
      for (var i = 0; i < this.children.length; i++) {
        owner = this.children[i].getInternalListOwner(id);
        if (owner) {
          return owner;
        }
      }
    },

    /*deleteAllChildren
     * function which deletes all children
     */
    deleteAllChildren: function(deleted) {
      if (!deleted) {
        deleted = [];
      }
      for (var i = this.children.length - 1; i >= 0; i--) {
        deleted.push.apply(deleted, this.children[i].deleteAllChildren());

        var d = this.removeChildNode(this.children[i], false, true);
        if (d) {
          deleted.push(d.deleteSelf());
        }

      }
      return deleted;
    },

    deleteSelf: function() {
      var data = GeometryNode.prototype.deleteSelf.call(this);
      this.children.length = 0;
      return data;
    },

    create: function(noInheritor) {
      var instance = this.geometryGenerator.getTargetClass(this.get('name'));
      var value = this.getValue();
      instance.setValue(value);

      for (var i = 0; i < this.children.length; i++) {
        var clone = this.children[i].create(noInheritor);
        instance.addChildNode(clone);
      }
      instance.childrenModified = true;
      instance.get('geom').transform(this.get('geom').matrix);
      instance.set('rendered', false);
      instance.reset();
      instance.render();

      return instance;
    },

    insertChild: function(index, child, registerUndo) {
      GeometryNode.prototype.insertChild.call(this, index, child, registerUndo);
      this.get('geom').insertChild(index, child.get('geom'));
      this.currentBounds = this.get('geom').bounds;
      this.bboxInvalid = true;
    },

    removeChildNode: function(node, registerUndo) {
      var removed = GeometryNode.prototype.removeChildNode.call(this, node, registerUndo);
      if (removed) {
        removed.get('geom').remove();
        this.currentBounds = this.get('geom').bounds;
        this.get('translationDelta').setValue(this.get('geom').position);
        this.stopListening(removed);
        this.bboxInvalid = true;
        return removed;
      }
    },

    unGroup: function() {

      var childClone = this.children.slice(0, this.children.length);
      var removedChildren = [];
      for (var i = 0; i < childClone.length; i++) {
        removedChildren.push(this.removeChildNode(childClone[i]));
      }

      return removedChildren;
    },

    setValue: function(data, registerUndo) {
      if (data.fillColor || data.strokeColor || data.strokeWidth) {
        var style_data = {};
        if (data.fillColor && !data.fillColor.noColor) {
          style_data.fillColor = data.fillColor;
        }
        if (data.strokeColor && !data.strokeColor.noColor) {
          style_data.strokeColor = data.strokeColor;
        }
        if (data.strokeWidth) {
          style_data.strokeWidth = data.strokeWidth;
        }
        for (var i = 0; i < this.children.length; i++) {
          this.children[i].setValue(style_data, registerUndo);
        }

      } else {
        GeometryNode.prototype.setValue.call(this, data, registerUndo);
      }
    },

    /* getValueFor
     * returns the actual value for a given property
     */
    getValueFor: function(property_name) {
      if (property_name == 'strokeColor' || property_name == 'fillColor' || property_name == 'strokeWidth') {
        return this.children[this.children.length - 1].getValueFor(property_name);
      } else {
        var property = this.get(property_name);
        return this.getValue()[property_name];
      }
    },

    //returns all non-group children
    getInstanceChildren: function(list) {
      if (!list) {
        list = [];
      }
      for (var i = 0; i < this.children.length; i++) {
        if (this.children[i].get('type') !== 'group') {
          list.push(this.children[i]);
        } else {
          this.children[i].getInstanceChildren(list);
        }

      }
      return list;
    },

    getChild: function(child) {
      if (child === this) {
        return this;
      }
      for (var i = 0; i < this.children.length; i++) {
        var m = this.children[i].getChild(child);
        if (m) {
          if (this.get('open')) {
            return m;
          } else {
            return this;
          }
        }
      }
      return null;
    },

    hasChild: function(child, top, last) {
      if (!top) {
        if (this === child) {
          return last;
        }
      }
      for (var i = 0; i < this.children.length; i++) {
        var child_found = this.children[i].hasChild(child, false, this);
        if (child_found) {
          return child_found;
        }
      }
    },

    toggleOpen: function() {
      this.set('open', true);
      if (!this.nodeParent.get('open')) {
        this.nodeParent.toggleOpen();
      }
      return true;
    },

    toggleClosed: function() {
      for (var i = 0; i < this.children.length; i++) {
        this.children[i].toggleClosed();
      }

      this.set('open', false);
      return true;
    },

    renderStyle: function() {
      if (!this.get('inFocus')) {
        this.get('geom').opacity = 0.5;
      } else {
        this.get('geom').opacity = 1;
      }
      this.get('geom').blendMode = this.get('blendMode_map')[this.get('blendMode').getValue()];

    },

    calculateCentroid: function() {
      var pointList = this.children.map(function(child) {
        return child.calculateCentroid();
      });
      return TrigFunc.centroid(pointList);
    },

  });

  return Group;
});<commit_msg>bug fix: duplicators of groups are now fixed.<commit_after>/*Group.js
 * path object
 * extends Duplicar node (for the time being)
 * node with actual path in it
 */

define([
  'underscore',
  'paper',
  'models/data/geometry/GeometryNode',
  'utils/TrigFunc',
  'models/data/properties/PFloat',
  'models/data/properties/PPoint',

], function(_, paper, GeometryNode, TrigFunc, PFloat, PPoint) {

  var Group = GeometryNode.extend({

    defaults: _.extend({}, GeometryNode.prototype.defaults, {

      name: 'group',
      type: 'geometry',
      points: null,
      open: false,

    }),

    initialize: function(attributes, options) {
      GeometryNode.prototype.initialize.apply(this, arguments);
      this.geometryGenerator = options.geometryGenerator;
      this.get('scalingDelta').setValue({
        x: 1,
        y: 1,
        operator: 'set'
      });
      var geom = new paper.Group();
      geom.applyMatrix = false;
      this.set('geom', geom);
      geom.data.instance = this;
      geom.data.geom = true;
      geom.data.nodetype = this.get('name');
      this.get('fillColor').setNoColor(true);
      this.get('strokeColor').setNoColor(true);
      this.get('strokeWidth').setValue(1);
      var ui_group = new paper.Group();
      var targetLayer = paper.project.layers.filter(function(layer) {
        return layer.name === 'ui_layer';
      })[0];
      targetLayer.addChild(ui_group);
      this.set('bbox', ui_group);
      this.createBBox();
      this.currentBounds = new paper.Rectangle(0, 0, 0, 0);
      /*
      this.startingUI = new paper.Path.Circle(new paper.Point(0,0),10);
      this.startingUI.fillColor = 'green';
      this.endingUI = this.startingUI.clone();
      this.endingUI.fillColor = 'red';*/

      this.bboxInvalid = false;
      this.childrenModified = false;
    },

    toJSON: function(noUndoCache) {
      var data = GeometryNode.prototype.toJSON.call(this, noUndoCache);
      data.matrix = this.get('geom').matrix.values;
      data.childrenModified = this.childrenModified;

      for (var i = 0; i < this.children.length; i++) {
        data.children.push(this.children[i].toJSON(noUndoCache));
      }
      return data;
    },

    clearUndoCache: function() {
      for (var i = 0; i < this.children.length; i++) {
        this.children[i].clearUndoCache();
      }
      GeometryNode.prototype.clearUndoCache.call(this);
    },

    parseJSON: function(data, manager) {
      var changed = GeometryNode.prototype.parseJSON.call(this, data, manager);
      var childClone = this.children.slice(0, this.children.length);
      var dataClone = data.children.slice(0, data.children.length);

      for (var i = 0; i < this.children.length; i++) {
        var target_id = this.children[i].get('id');
        var target_data = _.find(data.children, function(item) {
          return item.id == target_id;
        });
        //if the child currently exists in the group
        if (target_data) {
          var mI = this.children[i].parseJSON(target_data);
          changed.toRemove.push.apply(changed, mI.toRemove);
          changed.toAdd.push.apply(changed, mI.toAdd);
          childClone = _.filter(childClone, function(child) {
            return child.get('id') != target_id;
          });
          dataClone = _.filter(dataClone, function(data) {
            return data.id != target_id;
          });
        }
      }

      //remove children not in JSON
      for (var j = 0; j < childClone.length; j++) {
        var currentFuture = this.futureStates[this.futureStates.length - 1];
        var currentPast = this.previousStates[this.previousStates.length - 1];
        if (currentFuture) {
          var targetFuture = _.find(currentFuture.children, function(item) {
            return item.id == childClone[j].get('id');
          });
          if (targetFuture) {
            targetFuture.futureStates = childClone[j].futureStates;
            targetFuture.previousStates = childClone[j].previousStates;
          }
        }
        if (currentPast) {
          var targetPast = _.find(currentPast.children, function(item) {
            return item.id == childClone[j].get('id');
          });
          if (targetPast) {
            targetPast.futureStates = childClone[j].futureStates;
            targetPast.previousStates = childClone[j].previousStates;
          }
        }

        var removed = this.removeChildNode(childClone[j]);
        changed.toRemove.push(removed);
        removed.deleteSelf();
      }

      //addChildren in JSON that didn't already exist
      for (var k = 0; k < dataClone.length; k++) {
        var newChild = this.geometryGenerator.getTargetClass(dataClone[k].name);
        //TODO: not sure why this is needed..
        if (newChild.get('name') == 'duplicator') {
          newChild.stopListening(newChild.masterList);
          newChild.stopListening(newChild.internalList);
        }
        changed.toAdd.push(newChild);
        newChild.parseJSON(dataClone[k]);
        newChild.previousStates = dataClone[k].previousStates;
        newChild.futureStates = dataClone[k].futureStates;
        this.insertChild(dataClone[k].zIndex, newChild);
        if (data.matrix) {
          newChild.childrenModified = true;
          var matrix = new paper.Matrix(data.matrix[0], data.matrix[1], data.matrix[2], data.matrix[3], data.matrix[4], data.matrix[5]);
          newChild.get('geom').transform(matrix);
          newChild.set('rendered', false);
          newChild.reset();
          newChild.render();
        }
      }

      return changed;

    },

    parseInheritorJSON: function(data, manager) {
      for (var i = 0; i < this.children.length; i++) {
        var c_data = data.children[i];
        var child = this.children[i];
        child.parseInheritorJSON(c_data, manager);
      }
    },

    getById: function(id) {
      if (this.get('id') == id) {
        return this;
      } else {
        for (var i = 0; i < this.children.length; i++) {
          var match = this.children[i].getById(id);
          if (match) {
            return match;
          }
        }
      }
    },

    getInternalList: function(id) {
      var list;
      for (var i = 0; i < this.children.length; i++) {
        list = this.children[i].getInternalList(id);
        if (list) {
          return list;
        }
      }
    },

    getInternalListOwner: function(id) {
      var owner;
      for (var i = 0; i < this.children.length; i++) {
        owner = this.children[i].getInternalListOwner(id);
        if (owner) {
          return owner;
        }
      }
    },

    /*deleteAllChildren
     * function which deletes all children
     */
    deleteAllChildren: function(deleted) {
      if (!deleted) {
        deleted = [];
      }
      for (var i = this.children.length - 1; i >= 0; i--) {
        deleted.push.apply(deleted, this.children[i].deleteAllChildren());

        var d = this.removeChildNode(this.children[i], false, true);
        if (d) {
          deleted.push(d.deleteSelf());
        }

      }
      return deleted;
    },

    deleteSelf: function() {
      var data = GeometryNode.prototype.deleteSelf.call(this);
      this.children.length = 0;
      return data;
    },

    create: function(noInheritor) {
      var instance = this.geometryGenerator.getTargetClass(this.get('name'));
      var value = this.getValue();
      instance.setValue(value);

      for (var i = 0; i < this.children.length; i++) {
        var clone = this.children[i].create(noInheritor);
        instance.addChildNode(clone);
      }
      instance.childrenModified = true;
      instance.get('geom').transform(this.get('geom').matrix);
      instance.set('rendered', false);
      instance.reset();
      instance.render();

      return instance;
    },

    insertChild: function(index, child, registerUndo) {
      GeometryNode.prototype.insertChild.call(this, index, child, registerUndo);
      this.get('geom').insertChild(index, child.get('geom'));
      this.currentBounds = this.get('geom').bounds;
      this.bboxInvalid = true;
    },

    removeChildNode: function(node, registerUndo) {
      var removed = GeometryNode.prototype.removeChildNode.call(this, node, registerUndo);
      if (removed) {
        removed.get('geom').remove();
        this.currentBounds = this.get('geom').bounds;
        this.get('translationDelta').setValue(this.get('geom').position);
        this.stopListening(removed);
        this.bboxInvalid = true;
        return removed;
      }
    },

    unGroup: function() {

      var childClone = this.children.slice(0, this.children.length);
      var removedChildren = [];
      for (var i = 0; i < childClone.length; i++) {
        removedChildren.push(this.removeChildNode(childClone[i]));
      }

      return removedChildren;
    },

    setValue: function(data, registerUndo) {
      if (data.fillColor || data.strokeColor || data.strokeWidth) {
        var style_data = {};
        if (data.fillColor && !data.fillColor.noColor) {
          style_data.fillColor = data.fillColor;
        }
        if (data.strokeColor && !data.strokeColor.noColor) {
          style_data.strokeColor = data.strokeColor;
        }
        if (data.strokeWidth) {
          style_data.strokeWidth = data.strokeWidth;
        }
        for (var i = 0; i < this.children.length; i++) {
          this.children[i].setValue(style_data, registerUndo);
        }

      }

      GeometryNode.prototype.setValue.call(this, data, registerUndo);

    },

    /* getValueFor
     * returns the actual value for a given property
     */
    getValueFor: function(property_name) {
      if (property_name == 'strokeColor' || property_name == 'fillColor' || property_name == 'strokeWidth') {
        return this.children[this.children.length - 1].getValueFor(property_name);
      } else {
        var property = this.get(property_name);
        return this.getValue()[property_name];
      }
    },

    //returns all non-group children
    getInstanceChildren: function(list) {
      if (!list) {
        list = [];
      }
      for (var i = 0; i < this.children.length; i++) {
        if (this.children[i].get('type') !== 'group') {
          list.push(this.children[i]);
        } else {
          this.children[i].getInstanceChildren(list);
        }

      }
      return list;
    },

    getChild: function(child) {
      if (child === this) {
        return this;
      }
      for (var i = 0; i < this.children.length; i++) {
        var m = this.children[i].getChild(child);
        if (m) {
          if (this.get('open')) {
            return m;
          } else {
            return this;
          }
        }
      }
      return null;
    },

    hasChild: function(child, top, last) {
      if (!top) {
        if (this === child) {
          return last;
        }
      }
      for (var i = 0; i < this.children.length; i++) {
        var child_found = this.children[i].hasChild(child, false, this);
        if (child_found) {
          return child_found;
        }
      }
    },

    toggleOpen: function() {
      this.set('open', true);
      if (!this.nodeParent.get('open')) {
        this.nodeParent.toggleOpen();
      }
      return true;
    },

    toggleClosed: function() {
      for (var i = 0; i < this.children.length; i++) {
        this.children[i].toggleClosed();
      }

      this.set('open', false);
      return true;
    },

    renderStyle: function() {
      if (!this.get('inFocus')) {
        this.get('geom').opacity = 0.5;
      } else {
        this.get('geom').opacity = 1;
      }
      this.get('geom').blendMode = this.get('blendMode_map')[this.get('blendMode').getValue()];

    },

    calculateCentroid: function() {
      var pointList = this.children.map(function(child) {
        return child.calculateCentroid();
      });
      return TrigFunc.centroid(pointList);
    },

  });

  return Group;
});<|endoftext|>


<commit_before>'use strict';

import $ from 'jquery';
import { MediaQuery } from './foundation.util.mediaQuery';
import { Plugin } from './foundation.plugin';
import { GetYoDigits } from './foundation.util.core';

/**
 * Interchange module.
 * @module foundation.interchange
 * @requires foundation.util.mediaQuery
 */

class Interchange extends Plugin {
  /**
   * Creates a new instance of Interchange.
   * @class
   * @name Interchange
   * @fires Interchange#init
   * @param {Object} element - jQuery object to add the trigger to.
   * @param {Object} options - Overrides to the default plugin settings.
   */
  _setup(element, options) {
    this.$element = element;
    this.options = $.extend({}, Interchange.defaults, this.$element.data(), options);
    this.rules = [];
    this.currentPath = '';
    this.className = 'Interchange'; // ie9 back compat

    this._init();
    this._events();
  }

  /**
   * Initializes the Interchange plugin and calls functions to get interchange functioning on load.
   * @function
   * @private
   */
  _init() {
    MediaQuery._init();

    var id = this.$element[0].id || GetYoDigits(6, 'interchange');
    this.$element.attr({
      'data-resize': id,
      'id': id
    });

    this._parseOptions();
    this._addBreakpoints();
    this._generateRules();
    this._reflow();
  }

  /**
   * Initializes events for Interchange.
   * @function
   * @private
   */
  _events() {
    this.$element.off('resizeme.zf.trigger').on('resizeme.zf.trigger', () => this._reflow());
  }

  /**
   * Calls necessary functions to update Interchange upon DOM change
   * @function
   * @private
   */
  _reflow() {
    var match;

    // Iterate through each rule, but only save the last match
    for (var i in this.rules) {
      if(this.rules.hasOwnProperty(i)) {
        var rule = this.rules[i];
        if (window.matchMedia(rule.query).matches) {
          match = rule;
        }
      }
    }

    if (match) {
      this.replace(match.path);
    }
  }

  /**
   * Check options valifity and set defaults for:
   * - `data-interchange-type`: if set, enforce the type of replacement (auto, src, background or html)
   * @function
   * @private
   */
  _parseOptions() {
    var types = ['auto', 'src', 'background', 'html'];
    if (typeof this.options.type === undefined)
      this.options.type = 'auto';
    else if (types.indexOf(this.options.type) === -1) {
      console.log(`Warning: invalid value "${this.options.type}" for Interchange option "type"`);
      this.options.type = 'auto';
    }
  }

  /**
   * Gets the Foundation breakpoints and adds them to the Interchange.SPECIAL_QUERIES object.
   * @function
   * @private
   */
  _addBreakpoints() {
    for (var i in MediaQuery.queries) {
      if (MediaQuery.queries.hasOwnProperty(i)) {
        var query = MediaQuery.queries[i];
        Interchange.SPECIAL_QUERIES[query.name] = query.value;
      }
    }
  }

  /**
   * Checks the Interchange element for the provided media query + content pairings
   * @function
   * @private
   * @param {Object} element - jQuery object that is an Interchange instance
   * @returns {Array} scenarios - Array of objects that have 'mq' and 'path' keys with corresponding keys
   */
  _generateRules(element) {
    var rulesList = [];
    var rules;

    if (this.options.rules) {
      rules = this.options.rules;
    }
    else {
      rules = this.$element.data('interchange');
    }

    rules =  typeof rules === 'string' ? rules.match(/\[.*?\]/g) : rules;

    for (var i in rules) {
      if(rules.hasOwnProperty(i)) {
        var rule = rules[i].slice(1, -1).split(', ');
        var path = rule.slice(0, -1).join('');
        var query = rule[rule.length - 1];

        if (Interchange.SPECIAL_QUERIES[query]) {
          query = Interchange.SPECIAL_QUERIES[query];
        }

        rulesList.push({
          path: path,
          query: query
        });
      }
    }

    this.rules = rulesList;
  }

  /**
   * Update the `src` property of an image, or change the HTML of a container, to the specified path.
   * @function
   * @param {String} path - Path to the image or HTML partial.
   * @fires Interchange#replaced
   */
  replace(path) {
    if (this.currentPath === path) return;

    var trigger = 'replaced.zf.interchange';

    var type = this.options.type;
    if (type === 'auto') {
      if (this.$element[0].nodeName === 'IMG')
        type = 'src';
      else if (path.match(/\.(gif|jpg|jpeg|png|svg|tiff)([?#].*)?/i))
        type = 'background';
      else
        type = 'html';
    }

    // Replacing images
    if (type === 'src') {
      this.$element.attr('src', path)
        .on('load', () => { this.currentPath = path; })
        .trigger(trigger);
    }
    // Replacing background images
    else if (type === 'background') {
      path = path.replace(/\(/g, '%28').replace(/\)/g, '%29');
      this.$element
        .css({ 'background-image': 'url(' + path + ')' })
        .trigger(trigger);
    }
    // Replacing HTML
    else if (type === 'html') {
      $.get(path, (response) => {
        this.$element
          .html(response)
          .trigger(trigger);
        $(response).foundation();
        this.currentPath = path;
      });
    }

    /**
     * Fires when content in an Interchange element is done being loaded.
     * @event Interchange#replaced
     */
    // this.$element.trigger('replaced.zf.interchange');
  }

  /**
   * Destroys an instance of interchange.
   * @function
   */
  _destroy() {
    this.$element.off('resizeme.zf.trigger')
  }
}

/**
 * Default settings for plugin
 */
Interchange.defaults = {
  /**
   * Rules to be applied to Interchange elements. Set with the `data-interchange` array notation.
   * @option
   * @type {?array}
   * @default null
   */
  rules: null,

  /**
   * Type of the responsive ressource to replace. It can take the following options:
   * - `auto` (default): choose the type according to the element tag or the ressource extension,
   * - `src`: replace the `[src]` attribute, recommended for images `<img>`.
   * - `background`: replace the `background-image` CSS property.
   * - `html`: replace the element content.
   * @option
   * @type {string}
   * @default 'auto'
   */
  type: 'auto'
};

Interchange.SPECIAL_QUERIES = {
  'landscape': 'screen and (orientation: landscape)',
  'portrait': 'screen and (orientation: portrait)',
  'retina': 'only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx)'
};

export {Interchange};
<commit_msg>feat: add support for "JPEG" picture in Interchange<commit_after>'use strict';

import $ from 'jquery';
import { MediaQuery } from './foundation.util.mediaQuery';
import { Plugin } from './foundation.plugin';
import { GetYoDigits } from './foundation.util.core';

/**
 * Interchange module.
 * @module foundation.interchange
 * @requires foundation.util.mediaQuery
 */

class Interchange extends Plugin {
  /**
   * Creates a new instance of Interchange.
   * @class
   * @name Interchange
   * @fires Interchange#init
   * @param {Object} element - jQuery object to add the trigger to.
   * @param {Object} options - Overrides to the default plugin settings.
   */
  _setup(element, options) {
    this.$element = element;
    this.options = $.extend({}, Interchange.defaults, this.$element.data(), options);
    this.rules = [];
    this.currentPath = '';
    this.className = 'Interchange'; // ie9 back compat

    this._init();
    this._events();
  }

  /**
   * Initializes the Interchange plugin and calls functions to get interchange functioning on load.
   * @function
   * @private
   */
  _init() {
    MediaQuery._init();

    var id = this.$element[0].id || GetYoDigits(6, 'interchange');
    this.$element.attr({
      'data-resize': id,
      'id': id
    });

    this._parseOptions();
    this._addBreakpoints();
    this._generateRules();
    this._reflow();
  }

  /**
   * Initializes events for Interchange.
   * @function
   * @private
   */
  _events() {
    this.$element.off('resizeme.zf.trigger').on('resizeme.zf.trigger', () => this._reflow());
  }

  /**
   * Calls necessary functions to update Interchange upon DOM change
   * @function
   * @private
   */
  _reflow() {
    var match;

    // Iterate through each rule, but only save the last match
    for (var i in this.rules) {
      if(this.rules.hasOwnProperty(i)) {
        var rule = this.rules[i];
        if (window.matchMedia(rule.query).matches) {
          match = rule;
        }
      }
    }

    if (match) {
      this.replace(match.path);
    }
  }

  /**
   * Check options valifity and set defaults for:
   * - `data-interchange-type`: if set, enforce the type of replacement (auto, src, background or html)
   * @function
   * @private
   */
  _parseOptions() {
    var types = ['auto', 'src', 'background', 'html'];
    if (typeof this.options.type === undefined)
      this.options.type = 'auto';
    else if (types.indexOf(this.options.type) === -1) {
      console.log(`Warning: invalid value "${this.options.type}" for Interchange option "type"`);
      this.options.type = 'auto';
    }
  }

  /**
   * Gets the Foundation breakpoints and adds them to the Interchange.SPECIAL_QUERIES object.
   * @function
   * @private
   */
  _addBreakpoints() {
    for (var i in MediaQuery.queries) {
      if (MediaQuery.queries.hasOwnProperty(i)) {
        var query = MediaQuery.queries[i];
        Interchange.SPECIAL_QUERIES[query.name] = query.value;
      }
    }
  }

  /**
   * Checks the Interchange element for the provided media query + content pairings
   * @function
   * @private
   * @param {Object} element - jQuery object that is an Interchange instance
   * @returns {Array} scenarios - Array of objects that have 'mq' and 'path' keys with corresponding keys
   */
  _generateRules(element) {
    var rulesList = [];
    var rules;

    if (this.options.rules) {
      rules = this.options.rules;
    }
    else {
      rules = this.$element.data('interchange');
    }

    rules =  typeof rules === 'string' ? rules.match(/\[.*?\]/g) : rules;

    for (var i in rules) {
      if(rules.hasOwnProperty(i)) {
        var rule = rules[i].slice(1, -1).split(', ');
        var path = rule.slice(0, -1).join('');
        var query = rule[rule.length - 1];

        if (Interchange.SPECIAL_QUERIES[query]) {
          query = Interchange.SPECIAL_QUERIES[query];
        }

        rulesList.push({
          path: path,
          query: query
        });
      }
    }

    this.rules = rulesList;
  }

  /**
   * Update the `src` property of an image, or change the HTML of a container, to the specified path.
   * @function
   * @param {String} path - Path to the image or HTML partial.
   * @fires Interchange#replaced
   */
  replace(path) {
    if (this.currentPath === path) return;

    var trigger = 'replaced.zf.interchange';

    var type = this.options.type;
    if (type === 'auto') {
      if (this.$element[0].nodeName === 'IMG')
        type = 'src';
      else if (path.match(/\.(gif|jpe?g|jpeg|png|svg|tiff)([?#].*)?/i))
        type = 'background';
      else
        type = 'html';
    }

    // Replacing images
    if (type === 'src') {
      this.$element.attr('src', path)
        .on('load', () => { this.currentPath = path; })
        .trigger(trigger);
    }
    // Replacing background images
    else if (type === 'background') {
      path = path.replace(/\(/g, '%28').replace(/\)/g, '%29');
      this.$element
        .css({ 'background-image': 'url(' + path + ')' })
        .trigger(trigger);
    }
    // Replacing HTML
    else if (type === 'html') {
      $.get(path, (response) => {
        this.$element
          .html(response)
          .trigger(trigger);
        $(response).foundation();
        this.currentPath = path;
      });
    }

    /**
     * Fires when content in an Interchange element is done being loaded.
     * @event Interchange#replaced
     */
    // this.$element.trigger('replaced.zf.interchange');
  }

  /**
   * Destroys an instance of interchange.
   * @function
   */
  _destroy() {
    this.$element.off('resizeme.zf.trigger')
  }
}

/**
 * Default settings for plugin
 */
Interchange.defaults = {
  /**
   * Rules to be applied to Interchange elements. Set with the `data-interchange` array notation.
   * @option
   * @type {?array}
   * @default null
   */
  rules: null,

  /**
   * Type of the responsive ressource to replace. It can take the following options:
   * - `auto` (default): choose the type according to the element tag or the ressource extension,
   * - `src`: replace the `[src]` attribute, recommended for images `<img>`.
   * - `background`: replace the `background-image` CSS property.
   * - `html`: replace the element content.
   * @option
   * @type {string}
   * @default 'auto'
   */
  type: 'auto'
};

Interchange.SPECIAL_QUERIES = {
  'landscape': 'screen and (orientation: landscape)',
  'portrait': 'screen and (orientation: portrait)',
  'retina': 'only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx)'
};

export {Interchange};
<|endoftext|>


<commit_before>'use strict';

import $ from 'jquery';
import { transitionend } from './foundation.core.utils';

/**
 * Motion module.
 * @module foundation.motion
 */

const initClasses   = ['mui-enter', 'mui-leave'];
const activeClasses = ['mui-enter-active', 'mui-leave-active'];

const Motion = {
  animateIn: function(element, animation, cb) {
    animate(true, element, animation, cb);
  },

  animateOut: function(element, animation, cb) {
    animate(false, element, animation, cb);
  }
}

function Move(duration, elem, fn){
  var anim, prog, start = null;
  // console.log('called');

  if (duration === 0) {
    fn.apply(elem);
    elem.trigger('finished.zf.animate', [elem]).triggerHandler('finished.zf.animate', [elem]);
    return;
  }

  function move(ts){
    if(!start) start = ts;
    // console.log(start, ts);
    prog = ts - start;
    fn.apply(elem);

    if(prog < duration){ anim = window.requestAnimationFrame(move, elem); }
    else{
      window.cancelAnimationFrame(anim);
      elem.trigger('finished.zf.animate', [elem]).triggerHandler('finished.zf.animate', [elem]);
    }
  }
  anim = window.requestAnimationFrame(move);
}

/**
 * Animates an element in or out using a CSS transition class.
 * @function
 * @private
 * @param {Boolean} isIn - Defines if the animation is in or out.
 * @param {Object} element - jQuery or HTML object to animate.
 * @param {String} animation - CSS class to use.
 * @param {Function} cb - Callback to run when animation is finished.
 */
function animate(isIn, element, animation, cb) {
  element = $(element).eq(0);

  if (!element.length) return;

  var initClass = isIn ? initClasses[0] : initClasses[1];
  var activeClass = isIn ? activeClasses[0] : activeClasses[1];

  // Set up the animation
  reset();

  element
    .addClass(animation)
    .css('transition', 'none');

  requestAnimationFrame(() => {
    element.addClass(initClass);
    if (isIn) element.show();
  });

  // Start the animation
  requestAnimationFrame(() => {
    element[0].offsetWidth;
    element
      .css('transition', '')
      .addClass(activeClass);
  });

  // Clean up the animation when it finishes
  element.one(transitionend(element), finish);

  // Hides the element (for out animations), resets the element, and runs a callback
  function finish() {
    if (!isIn) element.hide();
    reset();
    if (cb) cb.apply(element);
  }

  // Resets transitions and removes motion-specific classes
  function reset() {
    element[0].style.transitionDuration = 0;
    element.removeClass(`${initClass} ${activeClass} ${animation}`);
  }
}

export { Move, Motion };

<commit_msg>refactor: remove unused offsetWidth property<commit_after>'use strict';

import $ from 'jquery';
import { transitionend } from './foundation.core.utils';

/**
 * Motion module.
 * @module foundation.motion
 */

const initClasses   = ['mui-enter', 'mui-leave'];
const activeClasses = ['mui-enter-active', 'mui-leave-active'];

const Motion = {
  animateIn: function(element, animation, cb) {
    animate(true, element, animation, cb);
  },

  animateOut: function(element, animation, cb) {
    animate(false, element, animation, cb);
  }
}

function Move(duration, elem, fn){
  var anim, prog, start = null;
  // console.log('called');

  if (duration === 0) {
    fn.apply(elem);
    elem.trigger('finished.zf.animate', [elem]).triggerHandler('finished.zf.animate', [elem]);
    return;
  }

  function move(ts){
    if(!start) start = ts;
    // console.log(start, ts);
    prog = ts - start;
    fn.apply(elem);

    if(prog < duration){ anim = window.requestAnimationFrame(move, elem); }
    else{
      window.cancelAnimationFrame(anim);
      elem.trigger('finished.zf.animate', [elem]).triggerHandler('finished.zf.animate', [elem]);
    }
  }
  anim = window.requestAnimationFrame(move);
}

/**
 * Animates an element in or out using a CSS transition class.
 * @function
 * @private
 * @param {Boolean} isIn - Defines if the animation is in or out.
 * @param {Object} element - jQuery or HTML object to animate.
 * @param {String} animation - CSS class to use.
 * @param {Function} cb - Callback to run when animation is finished.
 */
function animate(isIn, element, animation, cb) {
  element = $(element).eq(0);

  if (!element.length) return;

  var initClass = isIn ? initClasses[0] : initClasses[1];
  var activeClass = isIn ? activeClasses[0] : activeClasses[1];

  // Set up the animation
  reset();

  element
    .addClass(animation)
    .css('transition', 'none');

  requestAnimationFrame(() => {
    element.addClass(initClass);
    if (isIn) element.show();
  });

  // Start the animation
  requestAnimationFrame(() => {
    element
      .css('transition', '')
      .addClass(activeClass);
  });

  // Clean up the animation when it finishes
  element.one(transitionend(element), finish);

  // Hides the element (for out animations), resets the element, and runs a callback
  function finish() {
    if (!isIn) element.hide();
    reset();
    if (cb) cb.apply(element);
  }

  // Resets transitions and removes motion-specific classes
  function reset() {
    element[0].style.transitionDuration = 0;
    element.removeClass(`${initClass} ${activeClass} ${animation}`);
  }
}

export { Move, Motion };

<|endoftext|>