<commit_before>/**
 * Manage the blocks
 */
Blocks = function()
{
    var self = this;

    // View center & scale
    this.center = {};
    this.scale = 1.0;
    this.redrawTimeout = null;

    // History manager
    this.history = null;

    // Is the user dragging the view ?
    this.moving = null;

    // Is the system ready ?
    this.isReady = false;

    // Compact mode
    this.compactMode = false;

    // Context menu
    this.menu;

    // Mouse
    this.mouseX = 0;
    this.mouseY = 0;

    // Linking ?
    this.linking = null;

    // Selected items
    this.selectedLink = null;
    this.selectedSide = null;
    this.selectedBlock = null;

    // BLocks division
    this.div = null;

    // Context for drawingc
    this.context = null;

    // Blocks types
    this.blockTypes = [];

    // Instances
    this.blocks = [];

    // Edges
    this.edges = [];

    /**
     * Next block id
     */
    this.id = 1;

    /**
     * Clears blocks
     */
    this.clear = function()
    {
        this.edges = [];
        this.blocks = [];
        this.id = 1;
        this.div.find('.blocks').html('');
        this.redraw();
    }

    /**
     * Runs the blocks editor
     */
    this.run = function(selector)
    {
        $(document).ready(function() {
            self.div = $(selector);

            if (!self.div.size()) {
                alert('blocks.js: Unable to find ' + selector);
            }

            // Inject the initial editor
            self.div.html(
                  '
'
		+ '

'
                + '

'
                + '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"></svg>'
                + '

'
                + '
'
            );

            self.div.find('svg').attr('width',(self.div.width()));
            self.div.find('svg').attr('height',(self.div.height()));
            self.div.find('svg').width(self.div.width());
            self.div.find('svg').height(self.div.height());
            self.context = self.div.find('svg');

            // Setting up default viewer center
            self.center.x = self.div.width()/2;
            self.center.y = self.div.height()/2;

            // Run the menu
            self.menu = new BlocksMenu(self);

	    // Create the message handler
	    self.messages = new BlocksMessages(self.div.find('.messages'), self.div.width());

            // Listen for mouse position
            self.div[0].addEventListener('mousemove', function(evt) {
                self.mouseX = evt.pageX - self.div.offset().left;
                self.mouseY = evt.pageY - self.div.offset().top;
                self.move(evt);
            });

            $('html').mouseup(function(event) {
                if (event.which == 1) {
                    self.release();
                }
            });

            // Detect clicks on the canvas
            self.div.mousedown(function(evt) {
                if (self.canvasClicked()) {
                    evt.preventDefault();
                }
            });

            self.div.mousedown(function(event) {
                if (event.which == 2) {
                    self.moving = [self.mouseX, self.mouseY];
                }
            });

            self.div.mouseup(function(event) {
                if (event.which == 2) {
                    self.moving = null;
                }

                if (event.which == 1) {
                    self.menu.hide();
                }
            });

            // Initializing canvas
            self.context.svg();

            // Detecting key press
            $(document).keydown(function(e){
                if ($('input').is(':focus')) {
                    return;
                }

                // "del" will delete a selected link
                if (e.keyCode == 46) {
                    self.deleteEvent();
                }
            });

            // Binding the mouse wheel
            self.div.bind('mousewheel', function(event, delta, deltaX, deltaY) {
                var dX = self.mouseX - self.center.x;
                var dY = self.mouseY - self.center.y;
                var deltaScale = Math.pow(1.1, deltaY);
                self.center.x -= dX*(deltaScale-1);
                self.center.y -= dY*(deltaScale-1);
                self.scale *= deltaScale;
                self.redraw();
                event.preventDefault();
            });

            self.history = new History(self);
	    self.postReady();
        });
    };

    /**
     * Tell the system is ready
     */
    this.postReady = function()
    {
	this.isReady = true;
	if (this.readyQueue != undefined) {
	    for (k in this.readyQueue) {
		this.readyQueue[k]();
	    }
	}
    };

    /**
     * Callback when ready
     */
    this.ready = function(callback)
    {
	if (this.isReady) {
	    callback();
	} else {
	    var queue = [];
	    if (this.readyQueue != undefined) {
		queue = this.readyQueue;
	    }
	    queue.push(callback);
	    this.readyQueue = queue;
	}
    };

    /**
     * Gets the mouse position
     */
    this.getPosition = function()
    {
        var position = {};
        position.x = (self.mouseX-self.center.x)/self.scale;
        position.y = (self.mouseY-self.center.y)/self.scale;

        return position;
    };

    /**
     * Adds a block
     */
    this.addBlock = function(name, x, y)
    {
        for (k in self.blockTypes) {
            var type = self.blockTypes[k];

            if (type.name == name) {
                var block = new Block(self, self.blockTypes[k], this.id);
                block.x = x;
                block.y = y;
                block.create(self.div.find('.blocks'));
                this.history.save();
                this.blocks.push(block);
                this.id++;
            }
        }
    };

    /**
     * Registers a new block type
     */
    this.register = function(type)
    {
        self.blockTypes.push(new BlockType(type));
    };

    /**
     * Begin to draw an edge
     */
    this.beginLink = function(block, io)
    {
        this.linking = [block, io];
    };

    /**
     * The mouse has moved
     */
    this.move = function()
    {
        if (self.selectedSide) {
            var distance = Math.sqrt(Math.pow(this.mouseX-this.selectedSide[1],2)+Math.pow(this.mouseY-this.selectedSide[2],2));
            if (distance > 15) {
                var edge = this.edges[this.selectedLink];
                if (this.selectedSide[0] == 2) {
                    this.linking = [edge.block1, edge.io1];
                } else {
                    this.linking = [edge.block2, edge.io2];
                }

                this.removeEdge(this.selectedLink);
                this.selectedSide = null;
                this.selectedLink = null;
                this.redraw();
            }
        }

        if (self.moving) {
            self.center.x += (self.mouseX-self.moving[0]);
            self.center.y += (self.mouseY-self.moving[1]);
            self.moving = [self.mouseX, self.mouseY];
            self.redraw();
        }

        if (self.linking) {
            self.redraw();
        }
    };

    /**
     * Clicks the canvas
     */
    this.canvasClicked = function()
    {
        var prevent = false;
        this.selectedBlock = null;
        this.selectedLink = null;
        this.selectedSide = null;

        for (k in this.blocks) {
            var block = this.blocks[k];
            if (block.hasFocus) {
                this.selectedBlock = k;
            }
        }

        if (!this.selectedBlock) {
            for (k in this.edges) {
                var collide = this.edges[k].collide(this.mouseX, this.mouseY);
                if (collide != false) {
                    if (collide < 0.2) {
                        this.selectedSide = [1, this.mouseX, this.mouseY];
                    } else if (collide > 0.8) {
                        this.selectedSide = [2, this.mouseX, this.mouseY];
                    }
                    this.selectedLink = k;
                    prevent = true;
                    break;
                }
            }
        }

        this.redraw();
        return prevent;
    };

    /**
     * Edge to remove
     */
    this.removeEdge = function(edge)
    {
        this.edges[edge].erase();
        arrayRemove(this.edges, edge);
    };

    /**
     * Returns an edge id
     */
    this.getEdgeId = function(edge)
    {
	for (k in this.edges) {
	    if (edge == this.edges[k]) {
		return k;
	    }
	}

	return false;
    };

    /**
     * Remove a block
     */
    this.removeBlock = function(key)
    {
        var block = this.blocks[key];

        var newEdges = [];
        for (k in self.edges) {
            var edge = self.edges[k];
            if (edge.block1 == block || edge.block2 == block) {
                edge.erase();
            } else {
                newEdges.push(edge);
            }
        }
        this.edges = newEdges;

        block.erase();
        arrayRemove(this.blocks, this.selectedBlock);

        this.redraw();
    };

    /**
     * Get a block id
     */
    this.getBlockId = function(block)
    {
        for (k in self.blocks) {
            if (self.blocks[k] == block) {
                return k;
            }
        }

        return null;
    };

    /**
     * Retreive a block by ID
     */
    this.getBlockById = function (blockId)
    {
	for (k in self.blocks) {
	    if (self.blocks[k].id == blockId) {
		return self.blocks[k];
	    }
	}

	return null;
    };

    /**
     * Delete the current link
     */
    this.deleteEvent = function()
    {
        // Remove a block and its edges
        if (this.selectedBlock != null) {
            this.history.save();
            this.removeBlock(this.selectedBlock);
            this.selectedBlock = null;
        }

        // Remove an edge
        if (this.selectedLink != null) {
            this.history.save();
            this.removeEdge(this.selectedLink);
            this.selectedLink = null;
            this.redraw();
        }
    };

    /**
     * Do the redraw
     */
    this.doRedraw = function()
    {
        // Set the position for blocks
        for (k in self.blocks) {
            self.blocks[k].redraw(self.selectedBlock == k);
        }

        // Redraw edges
        var svg = self.context.svg('get');
        svg.clear();

        for (k in self.edges) {
            self.edges[k].draw(svg, self.selectedLink == k);
        }

        if (self.linking) {
            var position = this.linking[0].linkPositionFor(this.linking[1]);

            svg.line(position.x, position.y, self.mouseX, self.mouseY, {
                stroke: 'rgba(0,0,0,0.4)',
                strokeWidth: 3*self.scale
            });
        }

        self.redrawTimeout = null;
    };

    /**
     *  Draw the edges
     */
    this.redraw = function()
    {
        if (!this.redrawTimeout) {
            this.redrawTimeout = setTimeout(function() { self.doRedraw(); }, 20);
        }
    };

    /**
     * Release the mouse
     */
    this.release = function()
    {
        if (self.linking) {
            self.tryEndLink();
            self.linking=null;
        }
        self.redraw();
    };

    /**
     * Tries to end a link
     */
    this.tryEndLink = function()
    {
        for (k in self.blocks) {
            var block = self.blocks[k];
            if (block.hasFocus && block.focusedIo) {
                self.endLink(block, block.focusedIo);
                break;
            }
        }
    };

    /**
     * End drawing an edge
     */
    this.endLink = function(block, io)
    {
        try {
            if (this.linking[1].substr(0, 6) == 'output') {
                var edge = new Edge(this.linking[0], this.linking[1], block, io, self);
            } else {
                var edge = new Edge(block, io, this.linking[0], this.linking[1], self);
            }

            for (k in self.edges) {
                var other = self.edges[k];
                if (other.same(edge)) {
                    throw 'This edge already exists';
                }
            }

            var fromTo = edge.fromTo();
            if (fromTo[1].allSuccessors().indexOf(fromTo[0].id) != -1) {
                throw 'You can not create a loop';
            }

            this.history.save();
            edge.create();
            this.edges.push(edge);
        } catch (error) {
            this.messages.show('Unable to create this edge :' + "\n" + error, {'class': 'error'});
        }
        this.linking = null;
        this.selectedBlock = null;
        this.redraw();
    };

    /**
     * Changing the compact mode
     */
    this.toggleCompact = function()
    {
        this.compactMode = !this.compactMode;
        for (k in this.blocks) {
            this.blocks[k].render();
        }
        this.redraw();
    };

    /**
     * Export the scene
     */
    this.exportData = function()
    {
	var blocks = [];
	var edges = [];

	for (k in this.blocks) {
	    blocks.push(this.blocks[k].exportData());
	}

	for (k in this.edges) {
	    edges.push(this.edges[k].exportData());
	}

	return {
	    edges: edges,
	    blocks: blocks
	};
    };

    /**
     * Import some data
     */
    this.importData = function(scene)
    {
        this.clear();
        this.doLoad(scene, false);
    }

    /**
     * Lads a scene
     */
    this.load = function(scene)
    {
        this.doLoad(scene, true);
    }

    /**
     * Loads the scene
     */
    this.doLoad = function(scene, init)
    {
	self.ready(function() {
		var errors = [];
		self.id = 1;

		for (k in scene.blocks) {
		    try {
			var data = scene.blocks[k];
			var block = BlockImport(self, data);
			self.id = Math.max(self.id, block.id+1);
			block.create(self.div.find('.blocks'));
			self.blocks.push(block);
		    } catch (error) {
			errors.push('Block #'+k+ ':'+error);
		    }
		}

		for (k in scene.edges) {
		    try {
			var data = scene.edges[k];
			var edge = EdgeImport(self, data);
			edge.create();
			self.edges.push(edge);
		    } catch (error) {
			errors.push('Edge #'+k+' :'+error);
		    }
		}

		if (errors.length) {
		    var text = errors.length + " loading errors :
";
		    text += '
';
		    for (k in errors) {
			text += '
' + errors[k] + '
';
		    }
		    text += '
';
		    self.messages.show(text, {'class': 'error'});
		}

		self.redraw();

                if (init) {
		    self.perfectScale();
                }
	});
    };

    /**
     * Go to the perfect scale
     */
    this.perfectScale = function()
    {
	var xMin = null, xMax = null;
	var yMin = null, yMax = null;

	for (k in this.blocks) {
	    var block = this.blocks[k];
	    if (xMin == null) {
		xMin = xMax = block.x;
		yMin = yMax = block.y;
	    } else {
		xMin = Math.min(xMin, block.x);
		xMax = Math.max(xMax, block.x);
		yMin = Math.min(yMin, block.y);
		yMax = Math.max(yMax, block.y);
	    }
	}
	xMin -= 15;
	yMin -= 15;
	xMax += 200;
	yMax += 150;
	var scaleA = this.div.width()/(xMax-xMin);
	var scaleB = this.div.height()/(yMax-yMin);
	var scale = Math.min(scaleA, scaleB);

	this.scale = scale;
	this.center.x = this.div.width()/2 - scale*(xMin+xMax)/2.0;
	this.center.y = this.div.height()/2 - scale*(yMin+yMax)/2.0;

	this.redraw();
    }
};

<commit_msg>Securing linking from unsafe positions<commit_after>/**
 * Manage the blocks
 */
Blocks = function()
{
    var self = this;

    // View center & scale
    this.center = {};
    this.scale = 1.0;
    this.redrawTimeout = null;

    // History manager
    this.history = null;

    // Is the user dragging the view ?
    this.moving = null;

    // Is the system ready ?
    this.isReady = false;

    // Compact mode
    this.compactMode = false;

    // Context menu
    this.menu;

    // Mouse
    this.mouseX = 0;
    this.mouseY = 0;

    // Linking ?
    this.linking = null;

    // Selected items
    this.selectedLink = null;
    this.selectedSide = null;
    this.selectedBlock = null;

    // BLocks division
    this.div = null;

    // Context for drawingc
    this.context = null;

    // Blocks types
    this.blockTypes = [];

    // Instances
    this.blocks = [];

    // Edges
    this.edges = [];

    /**
     * Next block id
     */
    this.id = 1;

    /**
     * Clears blocks
     */
    this.clear = function()
    {
        this.edges = [];
        this.blocks = [];
        this.id = 1;
        this.div.find('.blocks').html('');
        this.redraw();
    }

    /**
     * Runs the blocks editor
     */
    this.run = function(selector)
    {
        $(document).ready(function() {
            self.div = $(selector);

            if (!self.div.size()) {
                alert('blocks.js: Unable to find ' + selector);
            }

            // Inject the initial editor
            self.div.html(
                  '
'
		+ '

'
                + '

'
                + '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"></svg>'
                + '

'
                + '
'
            );

            self.div.find('svg').attr('width',(self.div.width()));
            self.div.find('svg').attr('height',(self.div.height()));
            self.div.find('svg').width(self.div.width());
            self.div.find('svg').height(self.div.height());
            self.context = self.div.find('svg');

            // Setting up default viewer center
            self.center.x = self.div.width()/2;
            self.center.y = self.div.height()/2;

            // Run the menu
            self.menu = new BlocksMenu(self);

	    // Create the message handler
	    self.messages = new BlocksMessages(self.div.find('.messages'), self.div.width());

            // Listen for mouse position
            self.div[0].addEventListener('mousemove', function(evt) {
                self.mouseX = evt.pageX - self.div.offset().left;
                self.mouseY = evt.pageY - self.div.offset().top;
                self.move(evt);
            });

            $('html').mouseup(function(event) {
                if (event.which == 1) {
                    self.release();
                }
            });

            // Detect clicks on the canvas
            self.div.mousedown(function(evt) {
                if (self.canvasClicked()) {
                    evt.preventDefault();
                }
            });

            self.div.mousedown(function(event) {
                if (event.which == 2) {
                    self.moving = [self.mouseX, self.mouseY];
                }
            });

            self.div.mouseup(function(event) {
                if (event.which == 2) {
                    self.moving = null;
                }

                if (event.which == 1) {
                    self.menu.hide();
                }
            });

            // Initializing canvas
            self.context.svg();

            // Detecting key press
            $(document).keydown(function(e){
                if ($('input').is(':focus')) {
                    return;
                }

                // "del" will delete a selected link
                if (e.keyCode == 46) {
                    self.deleteEvent();
                }
            });

            // Binding the mouse wheel
            self.div.bind('mousewheel', function(event, delta, deltaX, deltaY) {
                var dX = self.mouseX - self.center.x;
                var dY = self.mouseY - self.center.y;
                var deltaScale = Math.pow(1.1, deltaY);
                self.center.x -= dX*(deltaScale-1);
                self.center.y -= dY*(deltaScale-1);
                self.scale *= deltaScale;
                self.redraw();
                event.preventDefault();
            });

            self.history = new History(self);
	    self.postReady();
        });
    };

    /**
     * Tell the system is ready
     */
    this.postReady = function()
    {
	this.isReady = true;
	if (this.readyQueue != undefined) {
	    for (k in this.readyQueue) {
		this.readyQueue[k]();
	    }
	}
    };

    /**
     * Callback when ready
     */
    this.ready = function(callback)
    {
	if (this.isReady) {
	    callback();
	} else {
	    var queue = [];
	    if (this.readyQueue != undefined) {
		queue = this.readyQueue;
	    }
	    queue.push(callback);
	    this.readyQueue = queue;
	}
    };

    /**
     * Gets the mouse position
     */
    this.getPosition = function()
    {
        var position = {};
        position.x = (self.mouseX-self.center.x)/self.scale;
        position.y = (self.mouseY-self.center.y)/self.scale;

        return position;
    };

    /**
     * Adds a block
     */
    this.addBlock = function(name, x, y)
    {
        for (k in self.blockTypes) {
            var type = self.blockTypes[k];

            if (type.name == name) {
                var block = new Block(self, self.blockTypes[k], this.id);
                block.x = x;
                block.y = y;
                block.create(self.div.find('.blocks'));
                this.history.save();
                this.blocks.push(block);
                this.id++;
            }
        }
    };

    /**
     * Registers a new block type
     */
    this.register = function(type)
    {
        self.blockTypes.push(new BlockType(type));
    };

    /**
     * Begin to draw an edge
     */
    this.beginLink = function(block, io)
    {
        this.linking = [block, io];
    };

    /**
     * The mouse has moved
     */
    this.move = function()
    {
        if (self.selectedSide) {
            var distance = Math.sqrt(Math.pow(this.mouseX-this.selectedSide[1],2)+Math.pow(this.mouseY-this.selectedSide[2],2));
            if (distance > 15) {
                var edge = this.edges[this.selectedLink];
                if (this.selectedSide[0] == 2) {
                    this.linking = [edge.block1, edge.io1];
                } else {
                    this.linking = [edge.block2, edge.io2];
                }

                this.removeEdge(this.selectedLink);
                this.selectedSide = null;
                this.selectedLink = null;
                this.redraw();
            }
        }

        if (self.moving) {
            self.center.x += (self.mouseX-self.moving[0]);
            self.center.y += (self.mouseY-self.moving[1]);
            self.moving = [self.mouseX, self.mouseY];
            self.redraw();
        }

        if (self.linking) {
            self.redraw();
        }
    };

    /**
     * Clicks the canvas
     */
    this.canvasClicked = function()
    {
        var prevent = false;
        this.selectedBlock = null;
        this.selectedLink = null;
        this.selectedSide = null;

        for (k in this.blocks) {
            var block = this.blocks[k];
            if (block.hasFocus) {
                this.selectedBlock = k;
            }
        }

        if (!this.selectedBlock) {
            for (k in this.edges) {
                var collide = this.edges[k].collide(this.mouseX, this.mouseY);
                if (collide != false) {
                    if (collide < 0.2) {
                        this.selectedSide = [1, this.mouseX, this.mouseY];
                    } else if (collide > 0.8) {
                        this.selectedSide = [2, this.mouseX, this.mouseY];
                    }
                    this.selectedLink = k;
                    prevent = true;
                    break;
                }
            }
        }

        this.redraw();
        return prevent;
    };

    /**
     * Edge to remove
     */
    this.removeEdge = function(edge)
    {
        this.edges[edge].erase();
        arrayRemove(this.edges, edge);
    };

    /**
     * Returns an edge id
     */
    this.getEdgeId = function(edge)
    {
	for (k in this.edges) {
	    if (edge == this.edges[k]) {
		return k;
	    }
	}

	return false;
    };

    /**
     * Remove a block
     */
    this.removeBlock = function(key)
    {
        var block = this.blocks[key];

        var newEdges = [];
        for (k in self.edges) {
            var edge = self.edges[k];
            if (edge.block1 == block || edge.block2 == block) {
                edge.erase();
            } else {
                newEdges.push(edge);
            }
        }
        this.edges = newEdges;

        block.erase();
        arrayRemove(this.blocks, this.selectedBlock);

        this.redraw();
    };

    /**
     * Get a block id
     */
    this.getBlockId = function(block)
    {
        for (k in self.blocks) {
            if (self.blocks[k] == block) {
                return k;
            }
        }

        return null;
    };

    /**
     * Retreive a block by ID
     */
    this.getBlockById = function (blockId)
    {
	for (k in self.blocks) {
	    if (self.blocks[k].id == blockId) {
		return self.blocks[k];
	    }
	}

	return null;
    };

    /**
     * Delete the current link
     */
    this.deleteEvent = function()
    {
        // Remove a block and its edges
        if (this.selectedBlock != null) {
            this.history.save();
            this.removeBlock(this.selectedBlock);
            this.selectedBlock = null;
        }

        // Remove an edge
        if (this.selectedLink != null) {
            this.history.save();
            this.removeEdge(this.selectedLink);
            this.selectedLink = null;
            this.redraw();
        }
    };

    /**
     * Do the redraw
     */
    this.doRedraw = function()
    {
        // Set the position for blocks
        for (k in self.blocks) {
            self.blocks[k].redraw(self.selectedBlock == k);
        }

        // Redraw edges
        var svg = self.context.svg('get');
        svg.clear();

        for (k in self.edges) {
            self.edges[k].draw(svg, self.selectedLink == k);
        }

        if (self.linking) {
            try {
                var position = this.linking[0].linkPositionFor(this.linking[1]);

                svg.line(position.x, position.y, self.mouseX, self.mouseY, {
                    stroke: 'rgba(0,0,0,0.4)',
                    strokeWidth: 3*self.scale
                });
            } catch (error) {
                self.linking = null;
            }
        }

        self.redrawTimeout = null;
    };

    /**
     *  Draw the edges
     */
    this.redraw = function()
    {
        if (!this.redrawTimeout) {
            this.redrawTimeout = setTimeout(function() { self.doRedraw(); }, 20);
        }
    };

    /**
     * Release the mouse
     */
    this.release = function()
    {
        if (self.linking) {
            self.tryEndLink();
            self.linking=null;
        }
        self.redraw();
    };

    /**
     * Tries to end a link
     */
    this.tryEndLink = function()
    {
        for (k in self.blocks) {
            var block = self.blocks[k];
            if (block.hasFocus && block.focusedIo) {
                self.endLink(block, block.focusedIo);
                break;
            }
        }
    };

    /**
     * End drawing an edge
     */
    this.endLink = function(block, io)
    {
        try {
            if (this.linking[1].substr(0, 6) == 'output') {
                var edge = new Edge(this.linking[0], this.linking[1], block, io, self);
            } else {
                var edge = new Edge(block, io, this.linking[0], this.linking[1], self);
            }

            for (k in self.edges) {
                var other = self.edges[k];
                if (other.same(edge)) {
                    throw 'This edge already exists';
                }
            }

            var fromTo = edge.fromTo();
            if (fromTo[1].allSuccessors().indexOf(fromTo[0].id) != -1) {
                throw 'You can not create a loop';
            }

            this.history.save();
            edge.create();
            this.edges.push(edge);
        } catch (error) {
            this.messages.show('Unable to create this edge :' + "\n" + error, {'class': 'error'});
        }
        this.linking = null;
        this.selectedBlock = null;
        this.redraw();
    };

    /**
     * Changing the compact mode
     */
    this.toggleCompact = function()
    {
        this.compactMode = !this.compactMode;
        for (k in this.blocks) {
            this.blocks[k].render();
        }
        this.redraw();
    };

    /**
     * Export the scene
     */
    this.exportData = function()
    {
	var blocks = [];
	var edges = [];

	for (k in this.blocks) {
	    blocks.push(this.blocks[k].exportData());
	}

	for (k in this.edges) {
	    edges.push(this.edges[k].exportData());
	}

	return {
	    edges: edges,
	    blocks: blocks
	};
    };

    /**
     * Import some data
     */
    this.importData = function(scene)
    {
        this.clear();
        this.doLoad(scene, false);
    }

    /**
     * Lads a scene
     */
    this.load = function(scene)
    {
        this.doLoad(scene, true);
    }

    /**
     * Loads the scene
     */
    this.doLoad = function(scene, init)
    {
	self.ready(function() {
		var errors = [];
		self.id = 1;

		for (k in scene.blocks) {
		    try {
			var data = scene.blocks[k];
			var block = BlockImport(self, data);
			self.id = Math.max(self.id, block.id+1);
			block.create(self.div.find('.blocks'));
			self.blocks.push(block);
		    } catch (error) {
			errors.push('Block #'+k+ ':'+error);
		    }
		}

		for (k in scene.edges) {
		    try {
			var data = scene.edges[k];
			var edge = EdgeImport(self, data);
			edge.create();
			self.edges.push(edge);
		    } catch (error) {
			errors.push('Edge #'+k+' :'+error);
		    }
		}

		if (errors.length) {
		    var text = errors.length + " loading errors :
";
		    text += '
';
		    for (k in errors) {
			text += '
' + errors[k] + '
';
		    }
		    text += '
';
		    self.messages.show(text, {'class': 'error'});
		}

		self.redraw();

                if (init) {
		    self.perfectScale();
                }
	});
    };

    /**
     * Go to the perfect scale
     */
    this.perfectScale = function()
    {
	var xMin = null, xMax = null;
	var yMin = null, yMax = null;

	for (k in this.blocks) {
	    var block = this.blocks[k];
	    if (xMin == null) {
		xMin = xMax = block.x;
		yMin = yMax = block.y;
	    } else {
		xMin = Math.min(xMin, block.x);
		xMax = Math.max(xMax, block.x);
		yMin = Math.min(yMin, block.y);
		yMax = Math.max(yMax, block.y);
	    }
	}
	xMin -= 15;
	yMin -= 15;
	xMax += 200;
	yMax += 150;
	var scaleA = this.div.width()/(xMax-xMin);
	var scaleB = this.div.height()/(yMax-yMin);
	var scale = Math.min(scaleA, scaleB);

	this.scale = scale;
	this.center.x = this.div.width()/2 - scale*(xMin+xMax)/2.0;
	this.center.y = this.div.height()/2 - scale*(yMin+yMax)/2.0;

	this.redraw();
    }
};

<|endoftext|>


<commit_before>/**
 * module.js - The core of module loader
 */

var cachedModules = seajs.cache = {}
var anonymousModuleMeta

var fetchingList = {}
var fetchedList = {}
var callbackList = {}
var waitingsList = {}

var STATUS = Module.STATUS = {
  "FETCHING": 1,    // The module file is being fetched now
  "SAVED": 2,       // The module data has been saved to cachedModules
  "LOADED": 3,      // The module and all its dependencies are ready to execute
  "EXECUTING": 4,   // The module is being executed
  "EXECUTED": 5     // The module is executed and `module.exports` is available
}

function Module(uri) {
  this.uri = uri
  this.dependencies = []
  this.exports = null
  this.status = 0
}

function resolve(ids, refUri) {
  if (isArray(ids)) {
    var ret = []
    for (var i = 0; i < ids.length; i++) {
      ret[i] = resolve(ids[i], refUri)
    }
    return ret
  }

  // Emit `resolve` event for plugins such as plugin-text
  var data = { id: ids, refUri: refUri }
  emit("resolve", data)

  return data.uri || id2Uri(data.id, refUri)
}

function use(uris, callback) {
  isArray(uris) || (uris = [uris])

  load(uris, function() {
    var exports = []

    for (var i = 0; i < uris.length; i++) {
      exports[i] = exec(cachedModules[uris[i]])
    }

    if (callback) {
      callback.apply(global, exports)
    }
  })
}

function load(uris, callback) {
  var unloadedUris = getUnloadedUris(uris)

  if (unloadedUris.length === 0) {
    callback()
    return
  }

  // Emit `load` event for plugins such as plugin-combo
  emit("load", unloadedUris)

  var len = unloadedUris.length
  var remain = len

  for (var i = 0; i < len; i++) {
    (function(uri) {
      var mod = cachedModules[uri]

      mod.status < STATUS.SAVED ? fetch(uri, onFetched) : onFetched()

      function onFetched() {
        // Maybe failed to fetch successfully, such as 404 error
        // In these cases, just call `cb` function directly
        if (mod.status < STATUS.SAVED) {
          done()
          return
        }

        // Break circular waiting callbacks
        if (isCircularWaiting(mod)) {
          printCircularLog(circularStack)
          circularStack.length = 0
          done(true)
          return
        }

        // Load all unloaded dependencies
        var waitings = waitingsList[uri] = getUnloadedUris(mod.dependencies)
        waitings.length ? load(waitings, done) : done()
      }

      function done(isCircularWaiting) {
        isCircularWaiting || (mod.status = STATUS.LOADED)

        if (--remain === 0) {
          callback()
        }
      }

    })(unloadedUris[i])
  }
}

function fetch(uri, callback) {
  cachedModules[uri].status = STATUS.FETCHING

  // Emit `fetch` event for plugins such as plugin-combo
  var data = { uri: uri }
  emit("fetch", data)
  var requestUri = data.requestUri || uri

  if (fetchedList[requestUri]) {
    callback()
    return
  }

  if (fetchingList[requestUri]) {
    callbackList[requestUri].push(callback)
    return
  }

  fetchingList[requestUri] = true
  callbackList[requestUri] = [callback]

  // Emit `request` event for plugins such as plugin-text
  var charset = configData.charset
  emit("request", data = {
    uri: uri,
    requestUri: requestUri,
    callback: onRequested,
    charset: charset
  })

  if (!data.requested) {
    request(data.requestUri, onRequested, charset)
  }

  function onRequested() {
    delete fetchingList[requestUri]
    fetchedList[requestUri] = true

    // Save meta data of anonymous module
    if (anonymousModuleMeta) {
      save(uri, anonymousModuleMeta)
      anonymousModuleMeta = undefined
    }

    // Call callbacks
    var fn, fns = callbackList[requestUri]
    delete callbackList[requestUri]
    while ((fn = fns.shift())) fn()
  }
}

function define(id, deps, factory) {
  var argsLen = arguments.length

  // HANDLE: define(factory)
  if (argsLen === 1) {
    factory = id
    id = undefined
  }
  // HANDLE: define(id || deps, factory)
  else if (argsLen === 2) {
    factory = deps
    deps = undefined

    // HANDLE: define(deps, factory)
    if (isArray(id)) {
      deps = id
      id = undefined
    }
  }

  // Try to derive uri in IE6-9 for anonymous modules
  if (!id && doc.attachEvent) {
    var script = getCurrentScript()

    if (script) {
      id = script.src
    }
    else {
      log("Failed to derive: " + factory)

      // NOTE: If the id-deriving methods above is failed, then falls back
      // to use onload event to get the uri
    }
  }

  // Parse dependencies according to the module factory code
  if (!isArray(deps) && isFunction(factory)) {
    deps = parseDependencies(factory.toString())
  }

  var meta = { id: id, deps: deps, factory: factory }

  id ? save(resolve(id), meta) :
      // Save information for "saving" work in the script onload event
      anonymousModuleMeta = meta
}

function save(uri, meta) {
  var mod = getModule(uri)

  // Do NOT override already saved modules
  if (mod.status < STATUS.SAVED) {
    // Let the id of anonymous module equal to its uri
    mod.id = meta.id || uri

    mod.dependencies = resolve(meta.deps || [], uri)
    mod.factory = meta.factory
    mod.status = STATUS.SAVED
  }
}

function exec(mod) {
  // Return `null` when `mod` is invalid
  if (!mod) {
    return null
  }

  // When module is executed, DO NOT execute it again. When module
  // is being executed, just return `module.exports` too, for avoiding
  // circularly calling
  if (mod.status >= STATUS.EXECUTING) {
    return mod.exports
  }

  mod.status = STATUS.EXECUTING

  function resolveInThisContext(id) {
    return resolve(id, mod.uri)
  }

  function require(id) {
    return exec(cachedModules[resolveInThisContext(id)])
  }

  require.resolve = resolveInThisContext

  require.async = function(ids, callback) {
    use(resolveInThisContext(ids), callback)
    return require
  }

  var factory = mod.factory

  var exports = isFunction(factory) ?
      factory(require, mod.exports = {}, mod) :
      factory

  mod.exports = exports === undefined ? mod.exports : exports
  mod.status = STATUS.EXECUTED

  return mod.exports
}

Module.prototype.destroy = function() {
  var uri = this.uri
  delete cachedModules[uri]
  delete fetchedList[uri]
}

// Helpers

function getModule(uri) {
  return cachedModules[uri] ||
      (cachedModules[uri] = new Module(uri))
}

function getUnloadedUris(uris) {
  var ret = []

  for (var i = 0; i < uris.length; i++) {
    var uri = uris[i]
    if (uri && getModule(uri).status < STATUS.LOADED) {
      ret.push(uri)
    }
  }

  return ret
}

var circularStack = []

function isCircularWaiting(mod) {
  var waitings = waitingsList[mod.uri] || []
  if (waitings.length === 0) {
    return false
  }

  circularStack.push(mod.uri)
  if (isOverlap(waitings, circularStack)) {
    cutWaitings(waitings)
    return true
  }

  for (var i = 0; i < waitings.length; i++) {
    if (isCircularWaiting(cachedModules[waitings[i]])) {
      return true
    }
  }

  circularStack.pop()
  return false
}

function isOverlap(arrA, arrB) {
  for (var i = 0; i < arrA.length; i++) {
    for (var j = 0; j < arrB.length; j++) {
      if (arrB[j] === arrA[i]) {
        return true
      }
    }
  }
  return false
}

function cutWaitings(waitings) {
  var uri = circularStack[0]

  for (var i = waitings.length - 1; i >= 0; i--) {
    if (waitings[i] === uri) {
      waitings.splice(i, 1)
      break
    }
  }
}

function printCircularLog(stack) {
  stack.push(stack[0])
  log("Circular dependencies: " + stack.join(" --> "))
}

function preload(callback) {
  var preloadMods = configData.preload
  var len = preloadMods.length

  if (len) {
    use(resolve(preloadMods), function() {
      // Remove the loaded preload modules
      preloadMods.splice(0, len)

      // Allow preload modules to add new preload modules
      preload(callback)
    })
  }
  else {
    callback()
  }
}

// Public API

seajs.use = function(ids, callback) {
  // Load preload modules before all other modules
  preload(function() {
    use(resolve(ids), callback)
  })
  return seajs
}

global.define = define

<commit_msg>Simplify arguments logic of define function<commit_after>/**
 * module.js - The core of module loader
 */

var cachedModules = seajs.cache = {}
var anonymousModuleMeta

var fetchingList = {}
var fetchedList = {}
var callbackList = {}
var waitingsList = {}

var STATUS = Module.STATUS = {
  "FETCHING": 1,    // The module file is being fetched now
  "SAVED": 2,       // The module data has been saved to cachedModules
  "LOADED": 3,      // The module and all its dependencies are ready to execute
  "EXECUTING": 4,   // The module is being executed
  "EXECUTED": 5     // The module is executed and `module.exports` is available
}

function Module(uri) {
  this.uri = uri
  this.dependencies = []
  this.exports = null
  this.status = 0
}

function resolve(ids, refUri) {
  if (isArray(ids)) {
    var ret = []
    for (var i = 0; i < ids.length; i++) {
      ret[i] = resolve(ids[i], refUri)
    }
    return ret
  }

  // Emit `resolve` event for plugins such as plugin-text
  var data = { id: ids, refUri: refUri }
  emit("resolve", data)

  return data.uri || id2Uri(data.id, refUri)
}

function use(uris, callback) {
  isArray(uris) || (uris = [uris])

  load(uris, function() {
    var exports = []

    for (var i = 0; i < uris.length; i++) {
      exports[i] = exec(cachedModules[uris[i]])
    }

    if (callback) {
      callback.apply(global, exports)
    }
  })
}

function load(uris, callback) {
  var unloadedUris = getUnloadedUris(uris)

  if (unloadedUris.length === 0) {
    callback()
    return
  }

  // Emit `load` event for plugins such as plugin-combo
  emit("load", unloadedUris)

  var len = unloadedUris.length
  var remain = len

  for (var i = 0; i < len; i++) {
    (function(uri) {
      var mod = cachedModules[uri]

      mod.status < STATUS.SAVED ? fetch(uri, onFetched) : onFetched()

      function onFetched() {
        // Maybe failed to fetch successfully, such as 404 error
        // In these cases, just call `cb` function directly
        if (mod.status < STATUS.SAVED) {
          done()
          return
        }

        // Break circular waiting callbacks
        if (isCircularWaiting(mod)) {
          printCircularLog(circularStack)
          circularStack.length = 0
          done(true)
          return
        }

        // Load all unloaded dependencies
        var waitings = waitingsList[uri] = getUnloadedUris(mod.dependencies)
        waitings.length ? load(waitings, done) : done()
      }

      function done(isCircularWaiting) {
        isCircularWaiting || (mod.status = STATUS.LOADED)

        if (--remain === 0) {
          callback()
        }
      }

    })(unloadedUris[i])
  }
}

function fetch(uri, callback) {
  cachedModules[uri].status = STATUS.FETCHING

  // Emit `fetch` event for plugins such as plugin-combo
  var data = { uri: uri }
  emit("fetch", data)
  var requestUri = data.requestUri || uri

  if (fetchedList[requestUri]) {
    callback()
    return
  }

  if (fetchingList[requestUri]) {
    callbackList[requestUri].push(callback)
    return
  }

  fetchingList[requestUri] = true
  callbackList[requestUri] = [callback]

  // Emit `request` event for plugins such as plugin-text
  var charset = configData.charset
  emit("request", data = {
    uri: uri,
    requestUri: requestUri,
    callback: onRequested,
    charset: charset
  })

  if (!data.requested) {
    request(data.requestUri, onRequested, charset)
  }

  function onRequested() {
    delete fetchingList[requestUri]
    fetchedList[requestUri] = true

    // Save meta data of anonymous module
    if (anonymousModuleMeta) {
      save(uri, anonymousModuleMeta)
      anonymousModuleMeta = undefined
    }

    // Call callbacks
    var fn, fns = callbackList[requestUri]
    delete callbackList[requestUri]
    while ((fn = fns.shift())) fn()
  }
}

function define(id, deps, factory) {
  // define(factory)
  if (arguments.length === 1) {
    factory = id
    id = undefined
  }

  // Try to derive uri in IE6-9 for anonymous modules
  if (!id && doc.attachEvent) {
    var script = getCurrentScript()

    if (script) {
      id = script.src
    }
    else {
      log("Failed to derive: " + factory)

      // NOTE: If the id-deriving methods above is failed, then falls back
      // to use onload event to get the uri
    }
  }

  // Parse dependencies according to the module factory code
  if (!isArray(deps) && isFunction(factory)) {
    deps = parseDependencies(factory.toString())
  }

  var meta = { id: id, deps: deps, factory: factory }

  id ? save(resolve(id), meta) :
      // Save information for "saving" work in the script onload event
      anonymousModuleMeta = meta
}

function save(uri, meta) {
  var mod = getModule(uri)

  // Do NOT override already saved modules
  if (mod.status < STATUS.SAVED) {
    // Let the id of anonymous module equal to its uri
    mod.id = meta.id || uri

    mod.dependencies = resolve(meta.deps || [], uri)
    mod.factory = meta.factory
    mod.status = STATUS.SAVED
  }
}

function exec(mod) {
  // Return `null` when `mod` is invalid
  if (!mod) {
    return null
  }

  // When module is executed, DO NOT execute it again. When module
  // is being executed, just return `module.exports` too, for avoiding
  // circularly calling
  if (mod.status >= STATUS.EXECUTING) {
    return mod.exports
  }

  mod.status = STATUS.EXECUTING

  function resolveInThisContext(id) {
    return resolve(id, mod.uri)
  }

  function require(id) {
    return exec(cachedModules[resolveInThisContext(id)])
  }

  require.resolve = resolveInThisContext

  require.async = function(ids, callback) {
    use(resolveInThisContext(ids), callback)
    return require
  }

  var factory = mod.factory

  var exports = isFunction(factory) ?
      factory(require, mod.exports = {}, mod) :
      factory

  mod.exports = exports === undefined ? mod.exports : exports
  mod.status = STATUS.EXECUTED

  return mod.exports
}

Module.prototype.destroy = function() {
  var uri = this.uri
  delete cachedModules[uri]
  delete fetchedList[uri]
}

// Helpers

function getModule(uri) {
  return cachedModules[uri] ||
      (cachedModules[uri] = new Module(uri))
}

function getUnloadedUris(uris) {
  var ret = []

  for (var i = 0; i < uris.length; i++) {
    var uri = uris[i]
    if (uri && getModule(uri).status < STATUS.LOADED) {
      ret.push(uri)
    }
  }

  return ret
}

var circularStack = []

function isCircularWaiting(mod) {
  var waitings = waitingsList[mod.uri] || []
  if (waitings.length === 0) {
    return false
  }

  circularStack.push(mod.uri)
  if (isOverlap(waitings, circularStack)) {
    cutWaitings(waitings)
    return true
  }

  for (var i = 0; i < waitings.length; i++) {
    if (isCircularWaiting(cachedModules[waitings[i]])) {
      return true
    }
  }

  circularStack.pop()
  return false
}

function isOverlap(arrA, arrB) {
  for (var i = 0; i < arrA.length; i++) {
    for (var j = 0; j < arrB.length; j++) {
      if (arrB[j] === arrA[i]) {
        return true
      }
    }
  }
  return false
}

function cutWaitings(waitings) {
  var uri = circularStack[0]

  for (var i = waitings.length - 1; i >= 0; i--) {
    if (waitings[i] === uri) {
      waitings.splice(i, 1)
      break
    }
  }
}

function printCircularLog(stack) {
  stack.push(stack[0])
  log("Circular dependencies: " + stack.join(" --> "))
}

function preload(callback) {
  var preloadMods = configData.preload
  var len = preloadMods.length

  if (len) {
    use(resolve(preloadMods), function() {
      // Remove the loaded preload modules
      preloadMods.splice(0, len)

      // Allow preload modules to add new preload modules
      preload(callback)
    })
  }
  else {
    callback()
  }
}

// Public API

seajs.use = function(ids, callback) {
  // Load preload modules before all other modules
  preload(function() {
    use(resolve(ids), callback)
  })
  return seajs
}

global.define = define

<|endoftext|>


<commit_before>"use strict";

class gsuiBlocksEdition {
	constructor( thisParent, elSelection ) {
		this.thisParent = thisParent;
		this._ = Object.seal( {
			mmFn: null,
			status: "",
			mdPageX: 0,
			mdPageY: 0,
			mmPageX: 0,
			mmPageY: 0,
			mdCurrTar: null,
			blcsMap: new Map(),
			selectionWhen: 0,
			selectionRowInd: 0,
			selectionElement: elSelection,
		} );
	}

	getBlocksMap() {
		return this._.blcsMap;
	}

	// Events to call manually:
	mousemove( e ) {
		this._.mmFn && this._.mmFn.call( this, e );
	}
	mousedown( e ) {
		const _ = this._;

		if ( e.button === 2 ) {
			const blc = this._getBlc( e.currentTarget );

			_.status = "deleting";
			_.mmFn = this._deletionMove;
			if ( blc ) {
				blc.classList.add( "gsui-block-hidden" );
				_.blcsMap.set( blc.dataset.id, blc );
			}
		} else if ( e.button === 0 ) {
			_.mdCurrTar = this._getBlc( e.currentTarget );
			if ( e.shiftKey ) {
				_.status = "selecting-1";
				_.mmFn = this._selectionStart;
				_.mdPageX = e.pageX;
				_.mdPageY = e.pageY;
				// _.mdCurrTar = this._getBlc( e.currentTarget );
				_.selectionWhen = this.thisParent.getWhenByPageX( e.pageX );
				_.selectionRowInd = this.thisParent.getRowIndexByPageY( e.pageY );
			} else if ( e.target.classList.contains( "gsui-block-cropB" ) ) {
				lg("cropB",_.mdCurrTar)
			}
		}
	}
	mouseup() {
		if ( this._.status === "selecting-1" ) {
			const blc = this._getBlc( this._.mdCurrTar );

			if ( blc ) {
				this._.blcsMap.set( blc.dataset.id, blc );
			}
		}
	}
	clear() {
		const _ = this._;

		_.mmFn = null;
		_.status = "";
		_.blcsMap.clear();
		_.selectionElement.classList.add( "gsuiBlocksEdition-selection-hidden" );
	}

	// private:
	_getBlc( el ) {
		if ( el.classList.contains( "gsui-block" ) ) {
			return el;
		} else if ( el.parentNode.classList.contains( "gsui-block" ) ) {
			return el.parentNode;
		}
	}
	_setMousemoveCoord( e ) {
		if ( e.type === "mousemove" ) {
			this._.mmPageX = e.pageX;
			this._.mmPageY = e.pageY;
		}
	}
	_deletionMove( e ) {
		const _ = this._,
			blc = this._getBlc( e.target );

		if ( blc && !blc.classList.contains( "gsui-block-hidden" ) ) {
			blc.classList.add( "gsui-block-hidden" );
			_.blcsMap.set( blc.dataset.id, blc );
		}
	}
	_selectionStart( e ) {
		const _ = this._;

		this._setMousemoveCoord( e );
		if ( Math.abs( _.mmPageX - _.mdPageX ) > 6 ||
			Math.abs( _.mmPageY - _.mdPageY ) > 6
		) {
			_.status = "selecting-2";
			_.mmFn = this._selectionMove;
			_.selectionElement.classList.remove( "gsuiBlocksEdition-selection-hidden" );
			this._selectionMove( e );
		}
	}
	_selectionMove( e ) {
		this._setMousemoveCoord( e );

		const _ = this._,
			thisP = this.thisParent,
			rowH = thisP.getRowHeight(),
			pxPB = thisP.getPxPerBeat(),
			st = _.selectionElement.style,
			rowIndB = thisP.getRowIndexByPageY( _.mmPageY ),
			whenB = thisP.getWhenByPageX( _.mmPageX ),
			topRow = Math.min( _.selectionRowInd, rowIndB ),
			bottomRow = Math.max( _.selectionRowInd, rowIndB ),
			when = Math.min( _.selectionWhen, whenB ),
			duration = 1 / thisP.getStepsPerBeat() + Math.abs( _.selectionWhen - whenB );

		st.top = topRow * rowH + "px";
		st.left = when * pxPB + "px";
		st.width = duration * pxPB + "px";
		st.height = ( bottomRow - topRow + 1 ) * rowH + "px";
		this._selection( when, duration, topRow, bottomRow );
	}
	_selection( when, duration, rowAInd, rowBInd ) {
		const thisP = this.thisParent,
			elBlcs = thisP.getBlocksElements(),
			elSelectedBlcs = thisP.getSelectedBlocksElements(),
			rowA = thisP.getRowByIndex( rowAInd ),
			rowB = thisP.getRowByIndex( rowBInd ),
			blcs = Object.entries( thisP.getBlocks() )
				.reduce( ( map, [ id, blc ] ) => {
					if ( !elSelectedBlcs.has( id ) &&
						blc.when < when + duration &&
						blc.when + blc.duration > when
					) {
						const elBlc = elBlcs.get( id ),
							pA = rowA.compareDocumentPosition( elBlc ),
							pB = rowB.compareDocumentPosition( elBlc );

						if ( pA & Node.DOCUMENT_POSITION_CONTAINED_BY ||
							pB & Node.DOCUMENT_POSITION_CONTAINED_BY || (
							pA & Node.DOCUMENT_POSITION_FOLLOWING &&
							pB & Node.DOCUMENT_POSITION_PRECEDING )
						) {
							elBlc.classList.add( "gsui-block-selected" );
							map.set( id, elBlc );
						}
					}
					return map;
				}, new Map );

		this._.blcsMap.forEach( ( blc, id ) => blc.classList
			.toggle( "gsui-block-selected", blcs.has( id ) ) );
		this._.blcsMap = blcs;
	}
}
<commit_msg>gsuiBlocksEdition: allow cropping<commit_after>"use strict";

class gsuiBlocksEdition {
	constructor( thisParent, callback, elSelection ) {
		this.thisParent = thisParent;
		this.callback = callback;
		this._ = Object.seal( {
			mmFn: null,
			status: "",
			valueA: null,
			valueB: null,
			mdWhen: 0,
			mmWhen: 0,
			mdPageX: 0,
			mdPageY: 0,
			mmPageX: 0,
			mmPageY: 0,
			blcsMap: new Map(),
			beatSnap: 0,
			mdCurrTar: null,
			editionRowMin: 0,
			editionRowMax: 0,
			editionCropMin: 0,
			editionWhenMin: 0,
			selectionRowInd: 0,
			selectionElement: elSelection,
		} );
	}

	getBlocksMap() {
		return this._.blcsMap;
	}

	// Events to call manually:
	mousemove( e ) {
		if ( this._.mmFn ) {
			if ( e.type === "mousemove" ) {
				this._.mmPageX = e.pageX;
				this._.mmPageY = e.pageY;
			}
			this._.mmWhen = this.thisParent.getWhenByPageX( this._.mmPageX );
			this._.mmFn.call( this, e );
		}
	}
	mousedown( e ) {
		const _ = this._,
			thisP = this.thisParent,
			blc = this._getBlc( e.currentTarget );

		if ( e.button === 2 ) {
			_.mmFn = this._deletionMove;
			_.status = "deleting";
			if ( blc ) {
				blc.classList.add( "gsui-block-hidden" );
				_.blcsMap.set( blc.dataset.id, blc );
			}
		} else if ( e.button === 0 ) {
			_.mdPageX = e.pageX;
			_.mdPageY = e.pageY;
			_.mdWhen = thisP.getWhenByPageX( e.pageX );
			if ( e.shiftKey ) {
				_.mmFn = this._selectionStart;
				_.status = "selecting-1";
				_.mdCurrTar = blc;
				_.selectionRowInd = thisP.getRowIndexByPageY( e.pageY );
			} else if ( e.target.classList.contains( "gsui-block-cropB" ) ) {
				const data = thisP.getBlocks();
				let min = Infinity;

				this._fillBlcsMap( blc ).forEach( ( _, id ) => {
					min = Math.min( min, data[ id ].duration );
				} );
				_.mmFn = this._cropBMove;
				_.status = "cropping-b";
				_.beatSnap = thisP.getBeatSnap();
				_.editionCropMin = -Math.max( 0, min - _.beatSnap );
			}
		}
	}
	mouseup() {
		const _ = this._,
			blcsMap = _.blcsMap;

		switch ( _.status ) {
			case "deleting":
				if ( blcsMap.size || this.thisParent.getSelectedBlocksElements().size ) {
					this.callback( "deleting", blcsMap );
				}
				break;
			case "cropping-a":
			case "cropping-b":
				if ( Math.abs( _.valueA ) > .000001 ) {
					this.callback( _.status, blcsMap, _.valueA );
				}
				break;
			case "selecting-2":
				_.selectionElement.classList.add( "gsuiBlocksEdition-selection-hidden" );
			case "selecting-1":
				if ( _.status === "selecting-1" && _.mdCurrTar ) {
					const blc = this._getBlc( _.mdCurrTar );

					if ( blc ) {
						blcsMap.set( blc.dataset.id, blc );
					}
				}
				if ( blcsMap.size ) {
					this.callback( "selecting", blcsMap );
				}
				break;
		}
		_.mmFn =
		_.valueA =
		_.valueB = null;
		_.status = "";
		blcsMap.clear();
	}

	// private:
	_getBlc( el ) {
		if ( el.classList.contains( "gsui-block" ) ) {
			return el;
		} else if ( el.parentNode.classList.contains( "gsui-block" ) ) {
			return el.parentNode;
		}
	}
	_fillBlcsMap( blc ) {
		const blcs = this._.blcsMap;

		if ( blc.classList.contains( "gsui-block-selected" ) ) {
			this.thisParent.getSelectedBlocksElements()
				.forEach( ( blc, id ) => blcs.set( id, blc ) );
		} else {
			blcs.set( blc.dataset.id, blc );
		}
		return blcs;
	}
	_cropBMove( e ) {
		const _ = this._,
			crop = Math.max( _.editionCropMin,
				Math.round( ( _.mmWhen - _.mdWhen ) / _.beatSnap ) * _.beatSnap );

		if ( crop !== _.valueA ) {
			const data = this.thisParent.getBlocks();

			_.valueA = crop;
			_.blcsMap.forEach( ( blc, id ) => (
				blc.style.width = data[ id ].duration + crop + "em"
			) );
		}
	}
	_deletionMove( e ) {
		const _ = this._,
			blc = this._getBlc( e.target );

		if ( blc && !blc.classList.contains( "gsui-block-hidden" ) ) {
			blc.classList.add( "gsui-block-hidden" );
			_.blcsMap.set( blc.dataset.id, blc );
		}
	}
	_selectionStart( e ) {
		const _ = this._;

		if ( Math.abs( _.mmPageX - _.mdPageX ) > 6 ||
			Math.abs( _.mmPageY - _.mdPageY ) > 6
		) {
			_.status = "selecting-2";
			_.mmFn = this._selectionMove;
			_.selectionElement.classList.remove( "gsuiBlocksEdition-selection-hidden" );
			this._selectionMove( e );
		}
	}
	_selectionMove( e ) {
		const _ = this._,
			thisP = this.thisParent,
			rowH = thisP.getRowHeight(),
			pxPB = thisP.getPxPerBeat(),
			st = _.selectionElement.style,
			rowIndB = thisP.getRowIndexByPageY( _.mmPageY ),
			topRow = Math.min( _.selectionRowInd, rowIndB ),
			bottomRow = Math.max( _.selectionRowInd, rowIndB ),
			when = Math.min( _.mdWhen, _.mmWhen ),
			duration = thisP.getBeatSnap() + Math.abs( _.mdWhen - _.mmWhen );

		st.top = topRow * rowH + "px";
		st.left = when * pxPB + "px";
		st.width = duration * pxPB + "px";
		st.height = ( bottomRow - topRow + 1 ) * rowH + "px";
		this._selection( when, duration, topRow, bottomRow );
	}
	_selection( when, duration, rowAInd, rowBInd ) {
		const thisP = this.thisParent,
			elBlcs = thisP.getBlocksElements(),
			elSelectedBlcs = thisP.getSelectedBlocksElements(),
			rowA = thisP.getRowByIndex( rowAInd ),
			rowB = thisP.getRowByIndex( rowBInd ),
			blcs = Object.entries( thisP.getBlocks() )
				.reduce( ( map, [ id, blc ] ) => {
					if ( !elSelectedBlcs.has( id ) &&
						blc.when < when + duration &&
						blc.when + blc.duration > when
					) {
						const elBlc = elBlcs.get( id ),
							pA = rowA.compareDocumentPosition( elBlc ),
							pB = rowB.compareDocumentPosition( elBlc );

						if ( pA & Node.DOCUMENT_POSITION_CONTAINED_BY ||
							pB & Node.DOCUMENT_POSITION_CONTAINED_BY || (
							pA & Node.DOCUMENT_POSITION_FOLLOWING &&
							pB & Node.DOCUMENT_POSITION_PRECEDING )
						) {
							elBlc.classList.add( "gsui-block-selected" );
							map.set( id, elBlc );
						}
					}
					return map;
				}, new Map );

		this._.blcsMap.forEach( ( blc, id ) => blc.classList
			.toggle( "gsui-block-selected", blcs.has( id ) ) );
		this._.blcsMap = blcs;
	}
}
<|endoftext|>


<commit_before>PinPoint.NotePresenter = function(note) {
  this.note = note;
  this.rootNodeType = "tr";
  this.childNodeType = "td";
}

PinPoint.NotePresenter.prototype = {
  present: function() {
    var noteNode = document.createElement(this.rootNodeType),
    timeNode = document.createElement(this.childNodeType),
    contentNode = document.createElement(this.childNodeType);

    noteNode.className = "note";

    timeNode.className = 'note_time';
    timeNode.textContent = this.note.noteTime;

    contentNode.className = 'note_content';
    contentNode.textContent = this.note.noteContent;

    noteNode.appendChild(timeNode);
    noteNode.appendChild(contentNode);

    return noteNode;
  }
}
<commit_msg>Added comments and removed irrelevant code<commit_after>PinPoint.NotePresenter = function(note) {
  this.note = note;
  this.rootNodeType = "tr";
  this.childNodeType = "td";
  this.linkNodeType = "a";
}

PinPoint.NotePresenter.prototype = {
  present: function() {
    // Creates a 'tr' node that represents a note
    var noteNode = document.createElement(this.rootNodeType),
    // Creates a 'td' node that represents the time and content in the note - when var is included, errors are thrown for some reason.
    timeAndContentNode = document.createElement(this.childNodeType);
    // Creates an 'a' node to link to a point in the video - when var is included, errors are thrown for some reason.
    linkNode = document.createElement(this.linkNodeType);
    // Assigns class names
    noteNode.className = "note";
    timeAndContentNode.className = 'note_info';

    // Gives the link an id.
    linkNode.setAttribute('id', 'link');
    // Sets the link to a specific time within the video
    linkNode.setAttribute('href', this.note.timeUrl );
    // Opens the link in a new tab
    linkNode.setAttribute('target', "_blank")
    // Creates the text for the link
    linkNode.innerHTML = "" + this.note.noteTime + "" + "" + this.note.noteContent + "";

    // Append the td 'timeAncContentNode' to the tr 'noteNode'
    noteNode.appendChild(timeAndContentNode);
    // Append the 'timeAncContentNode' to the tr 'noteNode'
    timeAndContentNode.appendChild(linkNode);

    return noteNode;
  }
}
<|endoftext|>


<commit_before>import {CommonJSAnalyser} from './CommonJSAnalyser.js';
import {Tokenizer} from '../tokenizer/Tokenizer.js';
import {FileReader} from '../fileReader/FileReader.js';
describe('CommonJS tokens analysis', function() {
  let assert = require('assert');
  it('should fail if the dependencies are not passed', function() {
    assert.throws(() => new CommonJSAnalyser(), /Unmet dependencies/);
  });
  it('should allow us to construct passing tokenizer', () => {
    assert(new CommonJSAnalyser(new Tokenizer(new FileReader)));
  });
  it('should return the modules required in a file', (done) => {
    let sample1 = __dirname + '/../../samples/commonJSSample1.js';
    let commonJS = new CommonJSAnalyser(new Tokenizer(new FileReader));
    commonJS.getDependencies(sample1)
        .then((tokens) => {
          assert.deepEqual(['file'], tokens);
          done();
        })
        .catch((reason) => {
          console.log(reason);
          assert(false);
          done();
        });
  });
  it ('should return the modules when the file has more than one dependency', (done) => {
    let sample2 = __dirname + '/../../samples/commonJSSample2.js';
    let commonJSAnalyser = new CommonJSAnalyser(new Tokenizer(new FileReader));
    commonJSAnalyser.getDependencies(sample2)
        .then((tokens) => {
          assert.equal(3, tokens.length);
          done();
        })
        .catch((reason) => {
          console.log(reason);
          assert(false);
          done();
      });
  });
});
<commit_msg>Rewritten the test to use the fat arrow instead of the usual function declaration for consistency<commit_after>import {CommonJSAnalyser} from './CommonJSAnalyser.js';
import {Tokenizer} from '../tokenizer/Tokenizer.js';
import {FileReader} from '../fileReader/FileReader.js';
describe('CommonJS tokens analysis', () => {
  let assert = require('assert');
  it('should fail if the dependencies are not passed', () => {
    assert.throws(() => new CommonJSAnalyser(), /Unmet dependencies/);
  });
  it('should allow us to construct passing tokenizer', () => {
    assert(new CommonJSAnalyser(new Tokenizer(new FileReader)));
  });
  it('should return the modules required in a file', (done) => {
    let sample1 = __dirname + '/../../samples/commonJSSample1.js';
    let commonJS = new CommonJSAnalyser(new Tokenizer(new FileReader));
    commonJS.getDependencies(sample1)
        .then((tokens) => {
          assert.deepEqual(['file'], tokens);
          done();
        })
        .catch((reason) => {
          console.log(reason);
          assert(false);
          done();
        });
  });
  it ('should return the modules when the file has more than one dependency', (done) => {
    let sample2 = __dirname + '/../../samples/commonJSSample2.js';
    let commonJSAnalyser = new CommonJSAnalyser(new Tokenizer(new FileReader));
    commonJSAnalyser.getDependencies(sample2)
        .then((tokens) => {
          assert.equal(3, tokens.length);
          done();
        })
        .catch((reason) => {
          console.log(reason);
          assert(false);
          done();
      });
  });
});
<|endoftext|>


<commit_before>/**
 * Copyright (c) Ajay Sreedhar. All rights reserved.
 *
 * Licensed under the MIT License.
 * Please see LICENSE file located in the project root for more information.
 */

'use strict';

import KongDash from './kongdash.js';
import FooterController from './controllers/footer.js';
import BootstrapController from './controllers/bootstrap.js';

const {ipcRenderer} = require('electron');

/**
 * Initializes the REST provider.
 *
 * @param {K_RESTProvider} restProvider - An instance of REST provider constructor.
 */
function initRESTProvider(restProvider) {
    const kongConfig = ipcRenderer.sendSync('get-config', 'kong');

    if (typeof kongConfig.username === 'string') {
        restProvider.setBasicAuth(kongConfig.username, kongConfig.password || '');
    }
}

KongDash.config(['restProvider', initRESTProvider]);

KongDash.controller('BootstrapController', ['$scope', '$element', '$base64', 'ajax', 'viewFrame', 'toast', BootstrapController]);
KongDash.controller('FooterController', ['$window', '$scope', '$http', 'viewFrame', 'toast', 'logger', FooterController]);
<commit_msg>remove: Use native base64 instead of angular module<commit_after>/**
 * Copyright (c) Ajay Sreedhar. All rights reserved.
 *
 * Licensed under the MIT License.
 * Please see LICENSE file located in the project root for more information.
 */

'use strict';

import KongDash from './kongdash.js';
import FooterController from './controllers/footer.js';
import BootstrapController from './controllers/bootstrap.js';

const {ipcRenderer} = require('electron');

/**
 * Initializes the REST provider.
 *
 * @param {K_RESTProvider} restProvider - An instance of REST provider constructor.
 */
function initRESTProvider(restProvider) {
    const kongConfig = ipcRenderer.sendSync('get-config', 'kong');

    if (typeof kongConfig.username === 'string') {
        restProvider.setBasicAuth(kongConfig.username, kongConfig.password || '');
    }
}

KongDash.config(['restProvider', initRESTProvider]);

KongDash.controller('BootstrapController', ['$scope', '$element', 'rest', 'viewFrame', 'toast', BootstrapController]);
KongDash.controller('FooterController', ['$window', '$scope', '$http', 'viewFrame', 'toast', 'logger', FooterController]);
<|endoftext|>


<commit_before>/*
MagJS v0.23.9
http://github.com/magnumjs/mag.js
(c) Michael Glazer
License: MIT
*/
(function(mag) {

  'use strict';

  var modules = [],
    controllers = []

  var mod = {
    innerMods :[],
    cache: []
  }

  mod.getState = function(index) {
    return modules[index][1]
  }
  mod.setState = function(index, state) {
    modules[index][1] = state
  }
  mod.getView = function(index) {
    return modules[index][0]
  }
  mod.getProps = function(index) {
    return modules[index][2]
  }
  mod.setProps = function(index, props) {
    return modules[index][2] = props
  }
  mod.remove = function(index) {
    modules.splice(index, 1)
  }
  mod.getId = function(index) {
    return modules[index] && modules[index][3]
  }
  mod.exists = function(index) {
    return typeof modules[index] != 'undefined' ? true : false
  }
  mod.setFrameId = function(index, fid) {
    modules[index][4] = fid;
  }
  mod.getFrameId = function(index) {
    return modules[index][4];
  }
  mod.submodule = function(id, index, module, props) {
    if (modules[index]) {
      // new call to existing
      // update props, merge with existing if same key
      if (props.key && props.key === mod.getProps(index).key) {
        mod.setProps(index, mag.utils.copy(mag.utils.merge(mod.getProps(index), props)));
      } else {
        mod.setProps(index, props);
      }
      // reinitialize the controller ?
      return modules[index]
    }
    modules[index] = [0, 0, 0, 0, 0]
    mod.setProps(index, props)
    var controller = function(context) {
        return (module.controller || function() {}).call(context, mod.getProps(index)) || context
      },
      view = function(index, state, ele) {
        module.view.call(module, state, mod.getProps(index), ele)
      }.bind({}, index),
      output = {
        controller: controller,
        view: view
      }
    modules[index][0] = output.view;
    modules[index][3] = id;
    modules[index][1] = getController(output.controller, index, id);
    // register the module
    return modules[index]
  }

  var timers = [];
  var prevs = [];

  function findMissing(change, element) {
    var prop = change.name;
    if (typeof change.object[change.name] == 'undefined') {
      // prop might be hierarchical?
      // getparent Object property chain?

      // get value of property from DOM
      var a = mag.fill.find(element, prop),
        greedy = prop[0] === '$',
        v, // can be an array, object or string
        // for each
        tmp = [];

      a.reverse().forEach(function(item, index) {
        if (item) {
          if (item.value && item.value.length > 0) {
            v = item.value
            if (item.type && (item.type == 'checkbox' || item.type == 'radio')) {
              v = {
                _value: v
              };
              if (item.checked) v._checked = true
              tmp.push(v)
            }
          } else if (item.innerText && item.innerText.length > 0) {
            v = item.innerText
          } else if (item.innerHTML && item.innerHTML.length > 0) {
            v = item.innerHTML
          } else if (!item.value && item.tagName == 'INPUT') {
            v = ''
          }
        }
      })
      if (tmp.length > 0 && greedy) return tmp
      return v
    }
  }

  function getController(ctrl, index, id) {
    var controller;
    if (typeof Proxy !== 'undefined') {

      var handler = function(type, index, change) {

        var current = JSON.stringify(change.object);
        var sname = String(change.name);
        if (current === prevs[index + sname]) {
          return;
        }
        prevs[index + sname] = current;

        if (change.type == 'get' && type!='props' && !~mag.fill.ignorekeys.indexOf(change.name.toString()) && typeof change.oldValue == 'undefined' && Object.keys(change.object).length === 0) {
          var res = findMissing(change, mag.doc.getElementById(mod.getId(index)));
          if (typeof res != 'undefined') {
            cached[index] = 0;
            return res;
          }
        }
        else if (change.type == 'set' && type!='props' && change.object[change.name] && change.object[change.name].draw && typeof change.object[change.name].draw == 'function') {

          // setting a state prop to a module

          // call unloader for module
          mod.innerMods[mod.getId(index)] = [change.name,change.object[change.name]];
        }

        // call setup handler
        var fun = mod.getFrameId(index);
        if (typeof fun == 'function' && change.type == 'set') {
          //debounce
          cancelAnimationFrame(timers[index]);
          timers[index] = requestAnimationFrame(fun);
        }

      };

      var base = mod.getProps(index);
      var baseP = mag.proxy(base, handler.bind({}, 'props', index));
      mod.setProps(index, baseP);

      var p = mag.proxy({}, handler.bind({}, 'state', index));

      controller = new ctrl(p);
    } else {
      controller = new ctrl({})
    }

    return controller;
  }

  mod.iscached = function(key) {
    var data = mag.utils.merge(mag.utils.copy(mod.getProps(key)), mod.getState(key));
    if (mod.cache[key] && mod.cache[key] === JSON.stringify(data)) {
      return true
    }
    mod.cache[key] = JSON.stringify(data);
  }

  mod.clear = function(key) {
    if (key > -1 && mod.cache[key]) {
      mod.cache.splice(key, 1)
    }
  }

  var cached = []
  mod.callView = function(node, index) {

    var module = mod.getView(index)

    if (!cached[index]) {
      mag.props.attachToArgs(index, mod.getState(index), node)
      cached[index] = 1
    }
    module(mod.getState(index), node)
  }

  mag.mod = mod;

}(mag));
<commit_msg>Update module.js<commit_after>/*
MagJS v0.23.9
http://github.com/magnumjs/mag.js
(c) Michael Glazer
License: MIT
*/
(function(mag) {

  'use strict';

  var modules = [],
    controllers = []

  var mod = {
    innerMods :[],
    cache: []
  }

  mod.getState = function(index) {
    return modules[index][1]
  }
  mod.setState = function(index, state) {
    modules[index][1] = state
  }
  mod.getView = function(index) {
    return modules[index][0]
  }
  mod.getProps = function(index) {
    return modules[index][2]
  }
  mod.setProps = function(index, props) {
    return modules[index][2] = props
  }
  mod.remove = function(index) {
    modules.splice(index, 1)
  }
  mod.getId = function(index) {
    return modules[index] && modules[index][3]
  }
  mod.exists = function(index) {
    return typeof modules[index] != 'undefined' ? true : false
  }
  mod.setFrameId = function(index, fid) {
    modules[index][4] = fid;
  }
  mod.getFrameId = function(index) {
    return modules[index][4];
  }
  mod.submodule = function(id, index, module, props) {
    if (modules[index]) {
      // new call to existing
      // update props, merge with existing if same key
      if (props.key && props.key === mod.getProps(index).key) {
        mod.setProps(index, mag.utils.merge(mod.getProps(index), props));
      } else {
        mod.setProps(index, props);
      }

      // reinitialize the controller ?
      return modules[index]
    }
    modules[index] = [0, 0, 0, 0, 0]
    mod.setProps(index, props)
    var controller = function(context) {
        return (module.controller || function() {}).call(context, mod.getProps(index)) || context
      },
      view = function(index, state, ele) {
        module.view.call(module, state, mod.getProps(index), ele)
      }.bind({}, index),
      output = {
        controller: controller,
        view: view
      }
    modules[index][0] = output.view;
    modules[index][3] = id;
    modules[index][1] = getController(output.controller, index, id);
    // register the module
    return modules[index]
  }

  var timers = [];
  var prevs = [];

  function findMissing(change, element) {
    var prop = change.name;
    if (typeof change.object[change.name] == 'undefined') {
      // prop might be hierarchical?
      // getparent Object property chain?

      // get value of property from DOM
      var a = mag.fill.find(element, prop),
        greedy = prop[0] === '$',
        v, // can be an array, object or string
        // for each
        tmp = [];

      a.reverse().forEach(function(item, index) {
        if (item) {
          if (item.value && item.value.length > 0) {
            v = item.value
            if (item.type && (item.type == 'checkbox' || item.type == 'radio')) {
              v = {
                _value: v
              };
              if (item.checked) v._checked = true
              tmp.push(v)
            }
          } else if (item.innerText && item.innerText.length > 0) {
            v = item.innerText
          } else if (item.innerHTML && item.innerHTML.length > 0) {
            v = item.innerHTML
          } else if (!item.value && item.tagName == 'INPUT') {
            v = ''
          }
        }
      })
      if (tmp.length > 0 && greedy) return tmp
      return v
    }
  }

  function getController(ctrl, index, id) {
    var controller;
    if (typeof Proxy !== 'undefined') {

      var handler = function(type, index, change) {

        var current = JSON.stringify(change.object);
        var sname = String(change.name);
        if (current === prevs[index + sname]) {
          return;
        }
        prevs[index + sname] = current;

        if (change.type == 'get' && type!='props' && !~mag.fill.ignorekeys.indexOf(change.name.toString()) && typeof change.oldValue == 'undefined' && Object.keys(change.object).length === 0) {
          var res = findMissing(change, mag.doc.getElementById(mod.getId(index)));
          if (typeof res != 'undefined') {
            cached[index] = 0;
            return res;
          }
        }
        else if (change.type == 'set' && type!='props' && change.object[change.name] && change.object[change.name].draw && typeof change.object[change.name].draw == 'function') {

          // setting a state prop to a module

          // call unloader for module
          mod.innerMods[mod.getId(index)] = [change.name,change.object[change.name]];
        }

        // call setup handler
        var fun = mod.getFrameId(index);
        if (typeof fun == 'function' && change.type == 'set') {
          //debounce
          cancelAnimationFrame(timers[index]);
          timers[index] = requestAnimationFrame(fun);
        }

      };

      var base = mod.getProps(index);
      var baseP = mag.proxy(base, handler.bind({}, 'props', index));
      mod.setProps(index, baseP);

      var p = mag.proxy({}, handler.bind({}, 'state', index));

      controller = new ctrl(p);
    } else {
      controller = new ctrl({})
    }

    return controller;
  }

  mod.iscached = function(key) {
    var data = mag.utils.merge(mag.utils.copy(mod.getProps(key)), mod.getState(key));
    if (mod.cache[key] && mod.cache[key] === JSON.stringify(data)) {
      return true
    }
    mod.cache[key] = JSON.stringify(data);
  }

  mod.clear = function(key) {
    if (key > -1 && mod.cache[key]) {
      mod.cache.splice(key, 1)
    }
  }

  var cached = []
  mod.callView = function(node, index) {

    var module = mod.getView(index)

    if (!cached[index]) {
      mag.props.attachToArgs(index, mod.getState(index), node)
      cached[index] = 1
    }
    module(mod.getState(index), node)
  }

  mag.mod = mod;

}(mag));
<|endoftext|>


<commit_before>var socket = io();
var reqAnimationFrame = (function () {
  return window[Hammer.prefixed(window, 'requestAnimationFrame')] || function (callback) {
    window.setTimeout(callback, 1000 / 60);
  };
})();

/**
 * Add a new shape on all clients. It doesn't immediately add a shape to your
 * DOM, the 'add' listener below handles that part.
 */
$('#add').click(function(e){
  socket.emit('add', {
    id: 'shape-' + Math.floor(Math.random() * 1000000000),
    opacity: $('#opacity').val(),
    backgroundColor: $('#color').val(),
    width: $('#size').val() + 'px',
    height: $('#size').val() + 'px',
    mixBlendMode: $('#mix-blend').val(),
    position: 'absolute'
  });
  e.preventDefault();
});

/**
 * Listen for new additions and add them to DOM.
 */
socket.on('add', function(props) {
  // Add the new element
  $('#canvas').append($('<div>')
    .addClass('shape')
    .attr('id', props.id)
    .css(props)
  );

  // Set up Hammer event listeners
  var el = document.getElementById(props.id);
  var initAngle = 0;
  var initScale = 1;
  var timer;
  var ticking = false;
  var transform = {
    x: 0,
    y: 0,
    z: 0,
    scale: initScale,
    angle: initAngle,
    rx: 0,
    ry: 0,
    rz: 0
  };

  var mc = new Hammer.Manager(el);

  mc.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
  mc.add(new Hammer.Rotate({ threshold: 0 })).recognizeWith(mc.get('pan'));
  mc.add(new Hammer.Pinch({ threshold: 0 })).recognizeWith([mc.get('pan'), mc.get('rotate')]);
  // mc.add(new Hammer.Tap({ event: 'doubletap', taps: 2 }));
  // mc.add(new Hammer.Tap());

  mc.on("panstart panmove", onPan);
  mc.on("rotatestart rotatemove", onRotate);
  mc.on("pinchstart pinchmove", onPinch);
  // mc.on("tap", onTap);
  // mc.on("doubletap", onDoubleTap);
  mc.on("hammer.input", function(ev) {
    if(ev.isFinal) {
      requestElementUpdate();
    }
  });

  /**
   * Paint the changes.
   */
  function requestElementUpdate(opts) {
    var emit = (opts === 'noEmit') ? 'noEmit' : null;

    if(!ticking) {
      reqAnimationFrame(updateElementTransform);
      ticking = true;

      // If needed, send position to everyone else.
      if (emit !== 'noEmit') {
        socket.emit('change', {
          me: socket.id,
          id: props.id,
          transform: transform
        });
      }
    }
  }

  /**
   * rAF callback which paints to the screen and emits the movement to others.
   */
  function updateElementTransform() {
    var value = [
      'translate3d(' + transform.x + 'px, ' + transform.y + 'px, ' + transform.z + 'px)',
      'scale(' + transform.scale + ', ' + transform.scale + ')',
      'rotate3d('+ transform.rx +','+ transform.ry +','+ transform.rz +','+  transform.angle + 'deg)'
    ];

    value = value.join(' ');
    el.style.webkitTransform = value;
    el.style.mozTransform = value;
    el.style.transform = value;
    ticking = false;
  }

  /**
   * Socket: listen for this shape to change.
   */
  socket.on('change', function(props) {
    if (props.me !== socket.id && props.id === el.id) {
      transform = props.transform;

      requestElementUpdate('noEmit');
    }
  });

  /**
   * Hammer: listen for pan
   */
  function onPan(ev) {
    if (ev.type == 'panstart') {
      initX = transform.x || 0;
      initY = transform.y || 0;
    }

    transform.x = parseInt(initX, 10) + parseInt(ev.deltaX, 10);
    transform.y = parseInt(initY, 10) + parseInt(ev.deltaY, 10);

    requestElementUpdate();
  }

  /**
   * Hammer: listen for pinch
   */
  function onPinch(ev) {
    if (ev.type == 'pinchstart') {
      initScale = transform.scale || 1;
    }

    transform.scale = initScale * ev.scale;

    requestElementUpdate();
  }

  /**
   * Hammer: listen for rotate
   */
  function onRotate(ev) {
    if (ev.type == 'rotatestart') {
      initAngle = transform.angle || 0;
    }

    transform.rz = 1;
    transform.angle = parseInt(initAngle, 10) + parseInt(ev.rotation, 10);

    requestElementUpdate();
  }

  /**
   * Hammer: listen for tap
   */
  // function onTap(ev) {
  //   clearTimeout(timer);

  //   timer = setTimeout(function () {
  //     requestElementUpdate();
  //   }, 200);

  //   requestElementUpdate();
  // }

  /**
   * Hammer: listen for double tap
   */
  // function onDoubleTap(ev) {
  //   transform.rx = 1;
  //   transform.angle = 80;

  //   clearTimeout(timer);
  //   timer = setTimeout(function () {
  //     requestElementUpdate();
  //   }, 500);
  //   requestElementUpdate();
  // }
});
<commit_msg>:grimacing: allow multi-shape movement on touch screen<commit_after>var socket = io();
var reqAnimationFrame = (function () {
  return window[Hammer.prefixed(window, 'requestAnimationFrame')] || function (callback) {
    window.setTimeout(callback, 1000 / 60);
  };
})();

/**
 * Add a new shape on all clients. It doesn't immediately add a shape to your
 * DOM, the 'add' listener below handles that part.
 */
$('#add').click(function(e){
  socket.emit('add', {
    id: 'shape-' + Math.floor(Math.random() * 1000000000),
    opacity: $('#opacity').val(),
    backgroundColor: $('#color').val(),
    width: $('#size').val() + 'px',
    height: $('#size').val() + 'px',
    mixBlendMode: $('#mix-blend').val(),
    position: 'absolute'
  });
  e.preventDefault();
});

/**
 * Listen for new additions and add them to DOM.
 */
socket.on('add', function(props) {
  // Add the new element
  $('#canvas').append($('<div>')
    .addClass('shape')
    .attr('id', props.id)
    .css(props)
  );

  // Set up Hammer event listeners
  var el = document.getElementById(props.id);
  var initX;
  var initY;
  var initAngle = 0;
  var initScale = 1;
  var timer;
  var ticking = false;
  var transform = {
    x: 0,
    y: 0,
    z: 0,
    scale: initScale,
    angle: initAngle,
    rx: 0,
    ry: 0,
    rz: 0
  };

  var mc = new Hammer.Manager(el);

  mc.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
  mc.add(new Hammer.Rotate({ threshold: 0 })).recognizeWith(mc.get('pan'));
  mc.add(new Hammer.Pinch({ threshold: 0 })).recognizeWith([mc.get('pan'), mc.get('rotate')]);
  // mc.add(new Hammer.Tap({ event: 'doubletap', taps: 2 }));
  // mc.add(new Hammer.Tap());

  mc.on("panstart panmove", onPan);
  mc.on("rotatestart rotatemove", onRotate);
  mc.on("pinchstart pinchmove", onPinch);
  // mc.on("tap", onTap);
  // mc.on("doubletap", onDoubleTap);
  mc.on("hammer.input", function(ev) {
    if(ev.isFinal) {
      requestElementUpdate();
    }
  });

  /**
   * Paint the changes.
   */
  function requestElementUpdate(opts) {
    var emit = (opts === 'noEmit') ? 'noEmit' : null;

    if(!ticking) {
      reqAnimationFrame(updateElementTransform);
      ticking = true;

      // If needed, send position to everyone else.
      if (emit !== 'noEmit') {
        socket.emit('change', {
          me: socket.id,
          id: props.id,
          transform: transform
        });
      }
    }
  }

  /**
   * rAF callback which paints to the screen and emits the movement to others.
   */
  function updateElementTransform() {
    var value = [
      'translate3d(' + transform.x + 'px, ' + transform.y + 'px, ' + transform.z + 'px)',
      'scale(' + transform.scale + ', ' + transform.scale + ')',
      'rotate3d('+ transform.rx +','+ transform.ry +','+ transform.rz +','+  transform.angle + 'deg)'
    ];

    value = value.join(' ');
    el.style.webkitTransform = value;
    el.style.mozTransform = value;
    el.style.transform = value;
    ticking = false;
  }

  /**
   * Socket: listen for this shape to change.
   */
  socket.on('change', function(props) {
    if (props.me !== socket.id && props.id === el.id) {
      transform = props.transform;

      requestElementUpdate('noEmit');
    }
  });

  /**
   * Hammer: listen for pan
   */
  function onPan(ev) {
    if (ev.type == 'panstart') {
      initX = transform.x || 0;
      initY = transform.y || 0;
    }

    if (ev.type == 'panmove') {
      transform.x = parseInt(initX, 10) + parseInt(ev.deltaX, 10);
      transform.y = parseInt(initY, 10) + parseInt(ev.deltaY, 10);
    }

    requestElementUpdate();
  }

  /**
   * Hammer: listen for pinch
   */
  function onPinch(ev) {
    if (ev.type == 'pinchstart') {
      initScale = transform.scale || 1;
    }

    transform.scale = initScale * ev.scale;

    requestElementUpdate();
  }

  /**
   * Hammer: listen for rotate
   */
  function onRotate(ev) {
    if (ev.type == 'rotatestart') {
      initAngle = transform.angle || 0;
    }

    transform.rz = 1;
    transform.angle = parseInt(initAngle, 10) + parseInt(ev.rotation, 10);

    requestElementUpdate();
  }

  /**
   * Hammer: listen for tap
   */
  // function onTap(ev) {
  //   clearTimeout(timer);

  //   timer = setTimeout(function () {
  //     requestElementUpdate();
  //   }, 200);

  //   requestElementUpdate();
  // }

  /**
   * Hammer: listen for double tap
   */
  // function onDoubleTap(ev) {
  //   transform.rx = 1;
  //   transform.angle = 80;

  //   clearTimeout(timer);
  //   timer = setTimeout(function () {
  //     requestElementUpdate();
  //   }, 500);
  //   requestElementUpdate();
  // }
});
<|endoftext|>


<commit_before>'use strict';

/**
 * restmod
 *
 * Usage - Model Definition:
 *
 *  module('module'.factory('PagedModel', ['$restModel', function($restModel) {
 *    return $restmodFactory()
 *      .on('before_collection_fetch', function() {
 *      });
 *      .classDefine('nextPage', function() {
 *      });
 *  }]));
 *
 *  module('module').factory('Book', ['$restModel', 'Author', Chapter', function($restModel, Author, Chapter) {
 *    return $restModel('api/books', {
 *      name: { primary: true },
 *      seen: { def: false, ignore: true },
 *      createdAt: { parse: 'railsDate' },
 *      chapters: { hasMany: Chapter },
 *      author: { hasOne: Author }
 *    }, function(_builder) {
 *      _builder.parse(function(_rawData, _localData) {
 *
 *      });
 *      _builder.render(function(_localData, _toData) {
 *
 *      });
 *      _builder.before_create(function() {
 *        // Do something with book before persisting!
 *        });
 *    }, PagedModel);
 *  }]);
 *
 * Usage - Services/Controllers:
 *
 *  module('module').service('Library', ['Author', 'Book', function(Book) {
 *
 *    ...
 *
 *    // Get all books with more than 100 pages
 *    books = Book.$search({ minPages: 100 })
 *
 *    // Get all chapters that belong to the Book with name 'angular-for-dummies'
 *    chapters = Author.mock('angular-for-dummies').chapters().fetch();
 *
 *    // Add a chapter to the same book
 *    chapter = Book.mock('angular-for-dummies').chapters().create({ title: 'Angular And IE7', pages: 30 });
 *
 *    // Modify a book's author (without fetching data)
 *    Book.mock('angular-for-dummies').author().update({ name: 'A. Dummy' });
 *
 *    // Modify a loaded book
 *    book.rating = 3;
 *    book.$save();
 *
 *    ...
 *
 *  }]);
 *
 */

angular.module('plRestmod', ['ng']);

<commit_msg>cleans up module.js<commit_after>'use strict';
angular.module('plRestmod', ['ng']);
<|endoftext|>