<commit_before>(function() {

    var Slider = function(options) {
        Backbone.View.call(this, options);
        this.sliderInitialize(options);
    };

    // Add the Slider to the kuba-bootstrap global namespace
    var root = this;
    root.kb = root.kb || {};
    root.kb.Slider = Slider;

    // Sniff touch events that indicate a mobile device
    var isMobile = 'ontouchend' in root;
    var downEvent = (isMobile) ? 'touchstart ' : 'mousedown ',
        moveEvent = (isMobile) ? 'touchmove ' : 'mousemove ',
        upEvent = (isMobile) ? 'touchend ' : 'mouseup ';

    Slider.extend = Backbone.View.extend;

    _.extend(Slider.prototype, Backbone.View.prototype, {
        // TODO Allow prefixes to be configured with an option
        // A blank prefix results in "transform" and is used by modern IE
        _prefixes: ['', '-webkit-', '-moz-'],
        sliderInitialize: function(options) {
            // Set sane options
            var o = this.options;
            // Element dimensions
            // Width and height are set dynamically in resetItems
            // But these options allow manual override
            this._width = _.isNumber(o.width) ? o.width : undefined;
            this._height = _.isNumber(o.height) ? o.height : undefined;

            this._paddingX = _.isNumber(o.paddingX) ? o.paddingX : 25;
            this._paddingY = _.isNumber(o.paddingY) ? o.paddingY : 50;
            this._force = _.isNumber(o.force) ? o.force : 4;
            this._threshold = _.isNumber(o.threshold) ? o.threshold : 5;

            // View and Collection
            // TODO confirm that these are backbone view / collection types
            this.collection || (this.collection = new Backbone.Collection());
            this.view = o.view || Backbone.View;

            // Inject the className
            // TODO This will overwrite any existing className, don't do this!
            // this.view.prototype.className = 'ease-box-double';

            // The lock will be set true when the slider is being moved
            this.lock = false;

            // Save the rendered view objects from each model
            this._views = [];

            // Default to a single row
            this._single = _.isBoolean(o.single) ? o.single : true;

            // Initialize private variables for scrolling
            this._pointerX = 0;
            this._pointerY = 0;
            this._scrollSurfacePos = 0;
            this._rightLimit = 0;

            // Render
            this.render();

            // Render the elements in the collection with the given view
            this.resetItems();

            // Check for any changes in viewport width
            // TODO on window?

            // Listen for 'add', 'remove' and 'reset' events in the collection
            // TODO 'reset' changed in backbone 1.0.0 to the smarter set()
            this.listenTo(this.collection, 'add', this.addItem);
            // TODO The remove event should be more specific than a reset
            this.listenTo(this.collection, 'remove', this.resetItems);
            this.listenTo(this.collection, 'reset', this.resetItems);
            // Also listen to sort events
            this.listenTo(this.collection, 'sort', this.resetItems);
        },
        sliderTemplate: '

',
        render: function() {
            // Render the inner slider
            this.$el.html(this.sliderTemplate);
            this.$scrollSurface = this.$('.con-slide-d');
            return this;
        },
        // TODO Combine functionality with resetItems / positionItems
        addItem: function(item) {
            // Allow child views to inspect parent properties (such as lock)
            var view = new this.view({model: item, parent: this});
            this._views.push(view);
            this.$scrollSurface.append(view.render().el);

            // TODO A better way to find index?
            var index = this.collection.length ? this.collection.indexOf(item) : 0;

            // If index is zero, reset the pointers
            if (index == 0) {
                this._pointerX = 0;
                this._pointerY = 1;
            }

            this.calculatePosition(view.$el, index);
        },
        resetItems: function() {
            // Reset existing items
            this.$scrollSurface.html('');
            this._views = [];
            this._rightLimit = 0;

            // Render all elements with the given view
            _.each(this.collection.models, function(m) {
                var view = new this.view({model: m, parent: this});
                // TODO better way to tie a model and its view together?
                this._views.push(view);
                this.$scrollSurface.append(view.render().el);
            }, this);

            // Determine the width and height of a box
            // Items must be the same size!
            if (this.options.width && this.options.height) {
                this._width = this.options.width;
                this._height = this.options.height;
            } else if (this._views.length) {
                this._width = this._views[0].$el.outerWidth();
                this._height = this._views[0].$el.outerHeight();
            }
            // TODO Error if neither are set

            // Also set the scroll surface position to 0 to prevent a smaller
            // number of items from being locked into position off-screen
            this.$scrollSurface.addClass('fx');
            this.moveScrollSurface(0);

            this.positionItems();

            // Enable events, only needs to be called once
            this.bindEvents();
        },
        positionItems: function() {
            // Reset variables
            this._columns = 0;

            // The _views array is populated by the resetItems function
            var len = this.collection.length;
            if (len) {
                this._pointerX = 0;
                this._pointerY = 1;
                _.each(this._views, function(view, index) {
                    this.calculatePosition(view.$el, index);
                }, this);
            }

            // Change positioning depending on number of rows
            // TODO columns does nothing
            if (!this._single) {
                this._columns = ((len - (len % 2)) / 2) + (len % 5);
            } else {
                this._columns = len;
            }
        },
        // TODO Allow click events to bubble up (either based on time
        // between mouse/touch up and down or deltaX == 0)
        bindEvents: function() {
            // Disable the slider events to prevent duplicate binds
            this.$el.off(downEvent + ' ' + upEvent);

            // Event variables, enclosed during assignment
            var initialX, viewport;
            var startX = 0;
            var currentX = 0;
            var deltaX = 0;
            var boxPos = 0;
            var self = this;

            // TODO Different handlers for mobile / desktop
            self.$el.on(downEvent, function(e) {
                startX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX;
                initialX = startX;

                // Remove the lock
                self.lock = false;

                // Prevent native text selection
                e.preventDefault();

                // Disallow slide if the viewport is wider than the elements
                viewport = self.$el.width();
                if (viewport > self._rightLimit) {
                    // TODO messages should be DEBUG only
                    // console.log('Viewport is wider than items, slider disabled');
                } else {
                    self.$el.on(moveEvent, function(e) {
                        currentX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX;
                        boxPos = 0;

                        deltaX = currentX - startX;
                        startX += deltaX;
                        boxPos = deltaX + self._scrollSurfacePos;

                        // Once the slider moves a threshold number of pixels,
                        // add a lock that child views can inspect
                        if (!self.lock) {
                            var diffX = currentX - initialX;
                            if (Math.abs(diffX) > self._threshold) self.lock = true;
                        }

                        // The function must be called with the View as context
                        self.$scrollSurface.removeClass('fx');
                        self.moveScrollSurface.call(self, boxPos);

                        // TODO The usage of boxPos seems overly complicated
                        self._scrollSurfacePos = boxPos;
                    });
                }

            // TODO Should also be called when mouse leaves frame
            }).on(upEvent, function(e) {
                // TODO Hard-coded threshold for movement
                // Only move if deltaX is greater than a specified threshold
                if (Math.abs(deltaX) > 3) {
                    boxPos = (deltaX * self._force) + self._scrollSurfacePos;
                }

                // If the content is smaller than the width of the viewport,
                // anchor the content to the left of the viewport
                if (viewport > self._rightLimit) {
                    boxPos = 0;
                } else {
                    var overflowWidth = self._rightLimit - viewport;
                    // If the viewport is to the left of the boxes, return the
                    // left edge of the viewport to the start of the boxes
                    if (boxPos > 0) {
                        boxPos = 0;
                    // If the viewport is to the right of the boxes, return
                    // the right edge of the viewport to the end of the boxes
                    } else if (boxPos < -overflowWidth) {
                        boxPos = -overflowWidth;
                    }
                }

                // The function must be called with the View as context
                self.$scrollSurface.addClass('fx');
                self.moveScrollSurface.call(self, boxPos);

                self._scrollSurfacePos = boxPos;

                // TODO Should be reset on mouse/touch down, not up?
                deltaX = 0;
                startX = 0;

                // Remove the move event handler
                self.$el.off(moveEvent);

                // Remove the slider "lock" after a tiny delay
                // setTimeout(function() { self.lock = false; });
            });
        },
        moveLeft: function(x) {
            var boxPos = this._scrollSurfacePos - x,
                viewport = this.$el.width();

            // If the content is smaller than the width of the viewport,
            // anchor the content to the left of the viewport
            if (viewport > this._rightLimit) {
                boxPos = 0;
            } else {
                var overflowWidth = this._rightLimit - viewport;
                // If the viewport is to the left of the boxes, return the
                // left edge of the viewport to the start of the boxes

                if (boxPos > 0) {
                    boxPos = 0;
                // If the viewport is to the right of the boxes, return
                // the right edge of the viewport to the end of the boxes
                } else if (boxPos < -overflowWidth) {
                    boxPos = -overflowWidth;
                }
            }

            this.moveScrollSurface.call(this, boxPos);
            this._scrollSurfacePos = boxPos;
        },
        moveRight: function(x) {
            var boxPos = this._scrollSurfacePos + x,
                viewport = this.$el.width();

            // If the content is smaller than the width of the viewport,
            // anchor the content to the left of the viewport
            if (viewport > this._rightLimit) {
                boxPos = 0;
            } else {
                var overflowWidth = this._rightLimit - viewport;
                // If the viewport is to the left of the boxes, return the
                // left edge of the viewport to the start of the boxes
                if (boxPos > 0) {
                    boxPos = 0;
                // If the viewport is to the right of the boxes, return
                // the right edge of the viewport to the end of the boxes
                } else if (boxPos < -overflowWidth) {
                    boxPos = -overflowWidth;
                }
            }

            this.moveScrollSurface.call(this, boxPos);
            this._scrollSurfacePos = boxPos;
        },
        moveScrollSurface: function(x) {
            // Create the cross-platform CSS transform
            // TODO Use CSS sniffing to reduce prefixes
            var transform = 'translate3d(' + x + 'px, 0px, 0px)';
            var styles = {};
            _.each(this._prefixes, function(prefix) {
                styles[prefix + 'transform'] = transform;
            });
            this.$scrollSurface.css(styles);
        },
        calculatePosition: function(box, index) {
            var pos = {};
            // TODO rem is block group?
            var rem = index % 1;

            // TODO Re-calculate for 'n' rows and integrate with _columns
            if (!this._single) {
                if (rem === 0){
                    if (this._pointerY == 0){
                        this._pointerY = 1;
                        this._pointerX = this._pointerX - 1;
                    } else {
                        this._pointerY = 0;
                    }
                }
            } else {
                this._pointerY = 0;
            }

            pos.x = (this._width * this._pointerX) + (this._paddingX * this._pointerX) + this._paddingX;
            pos.y = (this._paddingY + this._height) * this._pointerY;
            var rightEdge = pos.x + this._width + this._paddingX;
            // Save the x-coord of the right edge for the element farthest
            // to the right. This will be used for force / viewport focus.
            if (rightEdge > this._rightLimit) this._rightLimit = rightEdge;

            // Create the cross-platform CSS transform for box positioning
            var transform = 'translate3d(' + pos.x + 'px, ' + pos.y + 'px, 0px)';
            var styles = {};
            _.each(this._prefixes, function(prefix) {
                styles[prefix + 'transform'] = transform;
            });

            box.css(styles);

            this._pointerX++;
            return this;
        }
    });

    window.Slider = Slider;

}).call(this);
<commit_msg>reverse collection<commit_after>(function() {

    var Slider = function(options) {
        Backbone.View.call(this, options);
        this.sliderInitialize(options);
    };

    // Add the Slider to the kuba-bootstrap global namespace
    var root = this;
    root.kb = root.kb || {};
    root.kb.Slider = Slider;

    // Sniff touch events that indicate a mobile device
    var isMobile = 'ontouchend' in root;
    var downEvent = (isMobile) ? 'touchstart ' : 'mousedown ',
        moveEvent = (isMobile) ? 'touchmove ' : 'mousemove ',
        upEvent = (isMobile) ? 'touchend ' : 'mouseup ';

    Slider.extend = Backbone.View.extend;

    _.extend(Slider.prototype, Backbone.View.prototype, {
        // TODO Allow prefixes to be configured with an option
        // A blank prefix results in "transform" and is used by modern IE
        _prefixes: ['', '-webkit-', '-moz-'],
        sliderInitialize: function(options) {
            // Set sane options
            var o = this.options;
            // Element dimensions
            // Width and height are set dynamically in resetItems
            // But these options allow manual override
            this._width = _.isNumber(o.width) ? o.width : undefined;
            this._height = _.isNumber(o.height) ? o.height : undefined;

            this._paddingX = _.isNumber(o.paddingX) ? o.paddingX : 25;
            this._paddingY = _.isNumber(o.paddingY) ? o.paddingY : 50;
            this._force = _.isNumber(o.force) ? o.force : 4;
            this._threshold = _.isNumber(o.threshold) ? o.threshold : 5;

            // View and Collection
            // TODO confirm that these are backbone view / collection types
            this.collection || (this.collection = new Backbone.Collection());
            this.view = o.view || Backbone.View;

            // Inject the className
            // TODO This will overwrite any existing className, don't do this!
            // this.view.prototype.className = 'ease-box-double';

            // The lock will be set true when the slider is being moved
            this.lock = false;

            // Save the rendered view objects from each model
            this._views = [];

            // Default to a single row
            this._single = _.isBoolean(o.single) ? o.single : true;

            // Initialize private variables for scrolling
            this._pointerX = 0;
            this._pointerY = 0;
            this._scrollSurfacePos = 0;
            this._rightLimit = 0;

            // Render
            this.render();

            // Render the elements in the collection with the given view
            this.resetItems();

            // Check for any changes in viewport width
            // TODO on window?

            // Listen for 'add', 'remove' and 'reset' events in the collection
            // TODO 'reset' changed in backbone 1.0.0 to the smarter set()
            this.listenTo(this.collection, 'add', this.addItem);
            // TODO The remove event should be more specific than a reset
            this.listenTo(this.collection, 'remove', this.resetItems);
            this.listenTo(this.collection, 'reset', this.resetItems);
            // Also listen to sort events
            this.listenTo(this.collection, 'sort', this.resetItems);
        },
        sliderTemplate: '

',
        render: function() {
            // Render the inner slider
            this.$el.html(this.sliderTemplate);
            this.$scrollSurface = this.$('.con-slide-d');
            return this;
        },
        // TODO Combine functionality with resetItems / positionItems
        addItem: function(item) {
            // Allow child views to inspect parent properties (such as lock)
            var view = new this.view({model: item, parent: this});
            this._views.push(view);
            this.$scrollSurface.append(view.render().el);

            // TODO A better way to find index?
            var index = this.collection.length ? this.collection.indexOf(item) : 0;

            // If index is zero, reset the pointers
            if (index == 0) {
                this._pointerX = 0;
                this._pointerY = 1;
            }

            this.calculatePosition(view.$el, index);
        },
        resetItems: function() {
            // Reset existing items
            this.$scrollSurface.html('');
            this._views = [];
            this._rightLimit = 0;

            // Render all elements with the given view
            this.collection.models.reverse();

            _.each(this.collection.models, function(m) {
                var view = new this.view({model: m, parent: this});
                // TODO better way to tie a model and its view together?
                this._views.push(view);
                this.$scrollSurface.append(view.render().el);
            }, this);

            // Determine the width and height of a box
            // Items must be the same size!
            if (this.options.width && this.options.height) {
                this._width = this.options.width;
                this._height = this.options.height;
            } else if (this._views.length) {
                this._width = this._views[0].$el.outerWidth();
                this._height = this._views[0].$el.outerHeight();
            }
            // TODO Error if neither are set

            // Also set the scroll surface position to 0 to prevent a smaller
            // number of items from being locked into position off-screen
            this.$scrollSurface.addClass('fx');
            this.moveScrollSurface(0);

            this.positionItems();

            // Enable events, only needs to be called once
            this.bindEvents();
        },
        positionItems: function() {
            // Reset variables
            this._columns = 0;

            // The _views array is populated by the resetItems function
            var len = this.collection.length;
            if (len) {
                this._pointerX = 0;
                this._pointerY = 1;
                _.each(this._views, function(view, index) {
                    this.calculatePosition(view.$el, index);
                }, this);
            }

            // Change positioning depending on number of rows
            // TODO columns does nothing
            if (!this._single) {
                this._columns = ((len - (len % 2)) / 2) + (len % 5);
            } else {
                this._columns = len;
            }
        },
        // TODO Allow click events to bubble up (either based on time
        // between mouse/touch up and down or deltaX == 0)
        bindEvents: function() {
            // Disable the slider events to prevent duplicate binds
            this.$el.off(downEvent + ' ' + upEvent);

            // Event variables, enclosed during assignment
            var initialX, viewport;
            var startX = 0;
            var currentX = 0;
            var deltaX = 0;
            var boxPos = 0;
            var self = this;

            // TODO Different handlers for mobile / desktop
            self.$el.on(downEvent, function(e) {
                startX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX;
                initialX = startX;

                // Remove the lock
                self.lock = false;

                // Prevent native text selection
                e.preventDefault();

                // Disallow slide if the viewport is wider than the elements
                viewport = self.$el.width();
                if (viewport > self._rightLimit) {
                    // TODO messages should be DEBUG only
                    // console.log('Viewport is wider than items, slider disabled');
                } else {
                    self.$el.on(moveEvent, function(e) {
                        currentX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX;
                        boxPos = 0;

                        deltaX = currentX - startX;
                        startX += deltaX;
                        boxPos = deltaX + self._scrollSurfacePos;

                        // Once the slider moves a threshold number of pixels,
                        // add a lock that child views can inspect
                        if (!self.lock) {
                            var diffX = currentX - initialX;
                            if (Math.abs(diffX) > self._threshold) self.lock = true;
                        }

                        // The function must be called with the View as context
                        self.$scrollSurface.removeClass('fx');
                        self.moveScrollSurface.call(self, boxPos);

                        // TODO The usage of boxPos seems overly complicated
                        self._scrollSurfacePos = boxPos;
                    });
                }

            // TODO Should also be called when mouse leaves frame
            }).on(upEvent, function(e) {
                // TODO Hard-coded threshold for movement
                // Only move if deltaX is greater than a specified threshold
                if (Math.abs(deltaX) > 3) {
                    boxPos = (deltaX * self._force) + self._scrollSurfacePos;
                }

                // If the content is smaller than the width of the viewport,
                // anchor the content to the left of the viewport
                if (viewport > self._rightLimit) {
                    boxPos = 0;
                } else {
                    var overflowWidth = self._rightLimit - viewport;
                    // If the viewport is to the left of the boxes, return the
                    // left edge of the viewport to the start of the boxes
                    if (boxPos > 0) {
                        boxPos = 0;
                    // If the viewport is to the right of the boxes, return
                    // the right edge of the viewport to the end of the boxes
                    } else if (boxPos < -overflowWidth) {
                        boxPos = -overflowWidth;
                    }
                }

                // The function must be called with the View as context
                self.$scrollSurface.addClass('fx');
                self.moveScrollSurface.call(self, boxPos);

                self._scrollSurfacePos = boxPos;

                // TODO Should be reset on mouse/touch down, not up?
                deltaX = 0;
                startX = 0;

                // Remove the move event handler
                self.$el.off(moveEvent);

                // Remove the slider "lock" after a tiny delay
                // setTimeout(function() { self.lock = false; });
            });
        },
        moveLeft: function(x) {
            var boxPos = this._scrollSurfacePos - x,
                viewport = this.$el.width();

            // If the content is smaller than the width of the viewport,
            // anchor the content to the left of the viewport
            if (viewport > this._rightLimit) {
                boxPos = 0;
            } else {
                var overflowWidth = this._rightLimit - viewport;
                // If the viewport is to the left of the boxes, return the
                // left edge of the viewport to the start of the boxes

                if (boxPos > 0) {
                    boxPos = 0;
                // If the viewport is to the right of the boxes, return
                // the right edge of the viewport to the end of the boxes
                } else if (boxPos < -overflowWidth) {
                    boxPos = -overflowWidth;
                }
            }

            this.moveScrollSurface.call(this, boxPos);
            this._scrollSurfacePos = boxPos;
        },
        moveRight: function(x) {
            var boxPos = this._scrollSurfacePos + x,
                viewport = this.$el.width();

            // If the content is smaller than the width of the viewport,
            // anchor the content to the left of the viewport
            if (viewport > this._rightLimit) {
                boxPos = 0;
            } else {
                var overflowWidth = this._rightLimit - viewport;
                // If the viewport is to the left of the boxes, return the
                // left edge of the viewport to the start of the boxes
                if (boxPos > 0) {
                    boxPos = 0;
                // If the viewport is to the right of the boxes, return
                // the right edge of the viewport to the end of the boxes
                } else if (boxPos < -overflowWidth) {
                    boxPos = -overflowWidth;
                }
            }

            this.moveScrollSurface.call(this, boxPos);
            this._scrollSurfacePos = boxPos;
        },
        moveScrollSurface: function(x) {
            // Create the cross-platform CSS transform
            // TODO Use CSS sniffing to reduce prefixes
            var transform = 'translate3d(' + x + 'px, 0px, 0px)';
            var styles = {};
            _.each(this._prefixes, function(prefix) {
                styles[prefix + 'transform'] = transform;
            });
            this.$scrollSurface.css(styles);
        },
        calculatePosition: function(box, index) {
            var pos = {};
            // TODO rem is block group?
            var rem = index % 1;

            // TODO Re-calculate for 'n' rows and integrate with _columns
            if (!this._single) {
                if (rem === 0){
                    if (this._pointerY == 0){
                        this._pointerY = 1;
                        this._pointerX = this._pointerX - 1;
                    } else {
                        this._pointerY = 0;
                    }
                }
            } else {
                this._pointerY = 0;
            }

            pos.x = (this._width * this._pointerX) + (this._paddingX * this._pointerX) + this._paddingX;
            pos.y = (this._paddingY + this._height) * this._pointerY;
            var rightEdge = pos.x + this._width + this._paddingX;
            // Save the x-coord of the right edge for the element farthest
            // to the right. This will be used for force / viewport focus.
            if (rightEdge > this._rightLimit) this._rightLimit = rightEdge;

            // Create the cross-platform CSS transform for box positioning
            var transform = 'translate3d(' + pos.x + 'px, ' + pos.y + 'px, 0px)';
            var styles = {};
            _.each(this._prefixes, function(prefix) {
                styles[prefix + 'transform'] = transform;
            });

            box.css(styles);

            this._pointerX++;
            return this;
        }
    });

    window.Slider = Slider;

}).call(this);
<|endoftext|>


<commit_before>'use strict';

// ----------------------------------------------------------------------------

const assert = require ('assert');

//  ---------------------------------------------------------------------------

function testOHLCV (exchange, ohlcv, symbol, now) {

    const json = exchange.json (ohlcv);
    assert (ohlcv);
    assert (Array.isArray (ohlcv), json);
    const length = ohlcv.length;
    assert (length >= 6);
    for (let i = 0; i < ohlcv.length; i++) {
        assert ((ohlcv[i] === undefined) || (typeof ohlcv[i] === 'number'), json);
    }

    assert (ohlcv[0] > 1230940800000, json); // 03 Jan 2009 - first block
    assert (ohlcv[0] < 2147483648000, json); // 19 Jan 2038 - int32 overflows

    const skippedExchanges = [
        'bitmex', // BitMEX API docs: also note the open price is equal to the close price of the previous timeframe bucket.
        'vcc', // same as BitMEX, the open price is equal to the close price of the previous timeframe bucket.
    ];

    if (!exchange.inArray (exchange.id, skippedExchanges)) {
        assert ((ohlcv[1] === undefined) || (ohlcv[2] === undefined) || (ohlcv[1] <= ohlcv[2]), 'open > high, ' + exchange.safeString (ohlcv, 1, 'undefined') + ' > ' + exchange.safeString (ohlcv, 2, 'undefined')); // open <= high
    }

    assert ((ohlcv[3] === undefined) || (ohlcv[2] === undefined) || (ohlcv[3] <= ohlcv[2]), 'low > high, ' + exchange.safeString (ohlcv, 2, 'undefined') + ' > ' + exchange.safeString (ohlcv, 3, 'undefined')); // low <= high
    assert ((ohlcv[3] === undefined) || (ohlcv[4] === undefined) || (ohlcv[3] <= ohlcv[4]), 'low > close, ' + exchange.safeString (ohlcv, 3, 'undefined') + ' > ' + exchange.safeString (ohlcv, 4, 'undefined')); // low <= close
}

module.exports = testOHLCV;
<commit_msg>test.ohlcv.js skip delta<commit_after>'use strict';

// ----------------------------------------------------------------------------

const assert = require ('assert');

//  ---------------------------------------------------------------------------

function testOHLCV (exchange, ohlcv, symbol, now) {

    const json = exchange.json (ohlcv);
    assert (ohlcv);
    assert (Array.isArray (ohlcv), json);
    const length = ohlcv.length;
    assert (length >= 6);
    for (let i = 0; i < ohlcv.length; i++) {
        assert ((ohlcv[i] === undefined) || (typeof ohlcv[i] === 'number'), json);
    }

    assert (ohlcv[0] > 1230940800000, json); // 03 Jan 2009 - first block
    assert (ohlcv[0] < 2147483648000, json); // 19 Jan 2038 - int32 overflows

    const skippedExchanges = [
        'bitmex', // BitMEX API docs: also note the open price is equal to the close price of the previous timeframe bucket.
        'vcc', // same as BitMEX, the open price is equal to the close price of the previous timeframe bucket.
        'delta',
    ];

    if (!exchange.inArray (exchange.id, skippedExchanges)) {
        assert ((ohlcv[1] === undefined) || (ohlcv[2] === undefined) || (ohlcv[1] <= ohlcv[2]), 'open > high, ' + exchange.safeString (ohlcv, 1, 'undefined') + ' > ' + exchange.safeString (ohlcv, 2, 'undefined')); // open <= high
    }

    assert ((ohlcv[3] === undefined) || (ohlcv[2] === undefined) || (ohlcv[3] <= ohlcv[2]), 'low > high, ' + exchange.safeString (ohlcv, 2, 'undefined') + ' > ' + exchange.safeString (ohlcv, 3, 'undefined')); // low <= high
    assert ((ohlcv[3] === undefined) || (ohlcv[4] === undefined) || (ohlcv[3] <= ohlcv[4]), 'low > close, ' + exchange.safeString (ohlcv, 3, 'undefined') + ' > ' + exchange.safeString (ohlcv, 4, 'undefined')); // low <= close
}

module.exports = testOHLCV;
<|endoftext|>


<commit_before>(function () {
    'use strict';
    describe('Testing my game service', function () {
        var httpBackend,
            sandbox,
            $state,
            bingoTicket,
            gameService,
            bingoCall,
            coreApiConverter,
            gameProxy;

        beforeEach(function () {
            module('Tombola.Games.Bingo90.Game');
            module(function ($provide) {
                $provide.value('ProxyConstants', mocks.ProxyConstants);
            });

            inject(function ($injector) {
                httpBackend = $injector.get('$httpBackend');
                gameProxy = $injector.get('GameProxy');
                bingoTicket = $injector.get('BingoTicket');
                bingoCall = $injector.get('BingoCall');
                coreApiConverter = $injector.get('CoreApiConverter');
            });

        });

        it('Ensures the next game button works', function () {
            var returnedData = {
                "message": "NextGame",
                "payload": {
                    "gameId": 1,
                    "start": "2015-07-24T13:02:03.496Z",
                    "ticketPrice": 10
                }
            };
            $scope.nextGame();
            gameProxyStub.should.have.been.calledOnce;
            //coreApiConverter.convertNextGameData(returnedData);
            console.log('im here');
            goSpy.should.have.been.calledOnce.calledWithExactly('NextGame');

            deferred.resolve({message: "NextGame", payload: {gameId: 1, ticketPrice: 10, start: "2015-11-24T10:05:19.123Z"}});
            $rootScope.$digest();
        });

        it('Ensures the buy in game button works', function () {
            $scope.buyInGame();
            gameProxyStub.should.have.been.calledOnce;
        });

        afterEach(function () {
            sandbox.restore();
        });

    });

}());<commit_msg>Refactoring a test<commit_after>(function () {
    'use strict';
    describe('Testing my game service', function () {
        var httpBackend,
            sandbox,
            bingoTicket,
            bingoCall,
            coreApiConverter,
            gameProxy;

        beforeEach(function () {
            module('Tombola.Games.Bingo90.Game');
            module(function ($provide) {
                $provide.value('ProxyConstants', mocks.ProxyConstants);
            });

            inject(function ($injector) {
                httpBackend = $injector.get('$httpBackend');
                gameProxy = $injector.get('GameProxy');
                bingoTicket = $injector.get('BingoTicket');
                bingoCall = $injector.get('BingoCall');
                coreApiConverter = $injector.get('CoreApiConverter');
            });

        });

        it('Ensures the next game button works', function () {
            var returnedData = {
                "message": "NextGame",
                "payload": {
                    "gameId": 1,
                    "start": "2015-07-24T13:02:03.496Z",
                    "ticketPrice": 10
                }
            };
            $scope.nextGame();
            //coreApiConverter.convertNextGameData(returnedData);
            console.log('im here');
            goSpy.should.have.been.calledOnce.calledWithExactly('NextGame');

            deferred.resolve({message: "NextGame", payload: {gameId: 1, ticketPrice: 10, start: "2015-11-24T10:05:19.123Z"}});
            $rootScope.$digest();
        });

        it('Ensures the buy in game button works', function () {
            $scope.buyInGame();
        });

        afterEach(function () {
            sandbox.restore();
        });

    });

}());<|endoftext|>


<commit_before>/*
 Based on simpleheat, a tiny JavaScript library for drawing heatmaps with Canvas,
 by Vladimir Agafonkin
 https://github.com/mourner/simpleheat
*/

'use strict';

function torque_filters(canvas) {
    // jshint newcap: false, validthis: true
    if (!(this instanceof torque_filters)) { return new torque_filters(canvas); }

    this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas;

    this._ctx = canvas.getContext('2d');
    this._width = canvas.width;
    this._height = canvas.height;

    this._max = 1;
    this._data = [];
}

torque_filters.prototype = {

    defaultRadius: 25,

    defaultGradient: {
        0.4: 'blue',
        0.6: 'cyan',
        0.7: 'lime',
        0.8: 'yellow',
        1.0: 'red'
    },
    radius: function (r, blur) {
        blur = blur || 15;

        // create a grayscale blurred circle image that we'll use for drawing points
        var circle = this._circle = document.createElement('canvas'),
            ctx = circle.getContext('2d'),
            r2 = this._r = r + blur;

        circle.width = circle.height = r2 * 2;

        ctx.shadowOffsetX = ctx.shadowOffsetY = 200;
        ctx.shadowBlur = blur;
        ctx.shadowColor = 'black';

        ctx.beginPath();
        ctx.arc(r2 - 200, r2 - 200, r, 0, Math.PI * 2, true);
        ctx.closePath();
        ctx.fill();

        return this;
    },

    gradient: function (grad) {
        // create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
        var canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d'),
            gradient = ctx.createLinearGradient(0, 0, 0, 256);

        canvas.width = 1;
        canvas.height = 256;

        for (var i in grad) {
            gradient.addColorStop(i, grad[i]);
        }

        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, 1, 256);

        this._grad = ctx.getImageData(0, 0, 1, 256).data;

        return this;
    },

    draw: function () {
        if (!this._circle) {
            this.radius(this.defaultRadius);
        }
        if (!this._grad) {
            this.gradient(this.defaultGradient);
        }

        var ctx = this._ctx;
        var colored = ctx.getImageData(0, 0, this._canvas.width, this._canvas.height);
        this._colorize(colored.data, this._grad);
        ctx.putImageData(colored, 0, 0);

        return this;
    },

    _colorize: function (pixels, gradient) {
        for (var i = 3, len = pixels.length, j; i < len; i += 4) {
            j = pixels[i] * 4; // get gradient color from opacity value

            if (j) {
                pixels[i - 3] = gradient[j];
                pixels[i - 2] = gradient[j + 1];
                pixels[i - 1] = gradient[j + 2];
            }
        }
    }
};

module.exports = torque_filters;
<commit_msg>removes a bit more unneeded code from simpleheat<commit_after>/*
 Based on simpleheat, a tiny JavaScript library for drawing heatmaps with Canvas,
 by Vladimir Agafonkin
 https://github.com/mourner/simpleheat
*/

'use strict';

function torque_filters(canvas) {
    // jshint newcap: false, validthis: true
    if (!(this instanceof torque_filters)) { return new torque_filters(canvas); }

    this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas;

    this._ctx = canvas.getContext('2d');
    this._width = canvas.width;
    this._height = canvas.height;

    this._max = 1;
    this._data = [];
}

torque_filters.prototype = {

    defaultGradient: {
        0.4: 'blue',
        0.6: 'cyan',
        0.7: 'lime',
        0.8: 'yellow',
        1.0: 'red'
    },

    gradient: function (grad) {
        // create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
        var canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d'),
            gradient = ctx.createLinearGradient(0, 0, 0, 256);

        canvas.width = 1;
        canvas.height = 256;

        for (var i in grad) {
            gradient.addColorStop(i, grad[i]);
        }

        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, 1, 256);

        this._grad = ctx.getImageData(0, 0, 1, 256).data;

        return this;
    },

    draw: function () {
        if (!this._grad) {
            this.gradient(this.defaultGradient);
        }

        var ctx = this._ctx;
        var colored = ctx.getImageData(0, 0, this._canvas.width, this._canvas.height);
        this._colorize(colored.data, this._grad);
        ctx.putImageData(colored, 0, 0);

        return this;
    },

    _colorize: function (pixels, gradient) {
        for (var i = 3, len = pixels.length, j; i < len; i += 4) {
            j = pixels[i] * 4; // get gradient color from opacity value

            if (j) {
                pixels[i - 3] = gradient[j];
                pixels[i - 2] = gradient[j + 1];
                pixels[i - 1] = gradient[j + 2];
            }
        }
    }
};

module.exports = torque_filters;
<|endoftext|>


<commit_before>// ## Token types

// The assignment of fine-grained, information-carrying type objects
// allows the tokenizer to store the information it has about a
// token in a way that is very cheap for the parser to look up.

// All token type variables start with an underscore, to make them
// easy to recognize.

// The `beforeExpr` property is used to disambiguate between regular
// expressions and divisions. It is set on all token types that can
// be followed by an expression (thus, a slash after them would be a
// regular expression).
//
// `isLoop` marks a keyword as starting a loop, which is important
// to know when parsing a label, in order to allow or disallow
// continue jumps to that label.

export class TokenType {
  constructor(label, conf = {}) {
    this.label = label
    this.keyword = conf.keyword
    this.beforeExpr = !!conf.beforeExpr
    this.startsExpr = !!conf.startsExpr
    this.rightAssociative = !!conf.rightAssociative
    this.isLoop = !!conf.isLoop
    this.isAssign = !!conf.isAssign
    this.prefix = !!conf.prefix
    this.postfix = !!conf.postfix
    this.binop = conf.binop || null
    this.updateContext = null
  }
}

function binop(name, prec) {
  return new TokenType(name, {beforeExpr: true, binop: prec})
}
const beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true}

export const types = {
  num: new TokenType("num", startsExpr),
  regexp: new TokenType("regexp", startsExpr),
  string: new TokenType("string", startsExpr),
  name: new TokenType("name", startsExpr),
  eof: new TokenType("eof"),

  // Punctuation token types.
  bracketL: new TokenType("[", {beforeExpr: true, startsExpr: true}),
  bracketR: new TokenType("]"),
  braceL: new TokenType("{", {beforeExpr: true, startsExpr: true}),
  braceR: new TokenType("}"),
  parenL: new TokenType("(", {beforeExpr: true, startsExpr: true}),
  parenR: new TokenType(")"),
  comma: new TokenType(",", beforeExpr),
  semi: new TokenType(";", beforeExpr),
  colon: new TokenType(":", beforeExpr),
  dot: new TokenType("."),
  question: new TokenType("?", beforeExpr),
  arrow: new TokenType("=>", beforeExpr),
  template: new TokenType("template"),
  ellipsis: new TokenType("...", beforeExpr),
  backQuote: new TokenType("`", startsExpr),
  dollarBraceL: new TokenType("${", {beforeExpr: true, startsExpr: true}),
  at: new TokenType("@"),

  // Operators. These carry several kinds of properties to help the
  // parser use them properly (the presence of these properties is
  // what categorizes them as operators).
  //
  // `binop`, when present, specifies that this operator is a binary
  // operator, and will refer to its precedence.
  //
  // `prefix` and `postfix` mark the operator as a prefix or postfix
  // unary operator.
  //
  // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
  // binary operators with a very low precedence, that should result
  // in AssignmentExpression nodes.

  eq: new TokenType("=", {beforeExpr: true, isAssign: true}),
  assign: new TokenType("_=", {beforeExpr: true, isAssign: true}),
  incDec: new TokenType("++/--", {prefix: true, postfix: true, startsExpr: true}),
  prefix: new TokenType("prefix", {beforeExpr: true, prefix: true, startsExpr: true}),
  logicalOR: binop("||", 1),
  logicalAND: binop("&&", 2),
  bitwiseOR: binop("|", 3),
  bitwiseXOR: binop("^", 4),
  bitwiseAND: binop("&", 5),
  equality: binop("==/!=", 6),
  relational: binop("</>", 7),
  bitShift: binop("<</>>", 8),
  plusMin: new TokenType("+/-", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),
  modulo: binop("%", 10),
  star: binop("*", 10),
  slash: binop("/", 10),
  exponent: new TokenType("**", {beforeExpr: true, binop: 11, rightAssociative: true})
}

// Map keyword names to token types.

export const keywords = {}

// Succinct definitions of keyword token types
function kw(name, options = {}) {
  options.keyword = name
  keywords[name] = types["_" + name] = new TokenType(name, options)
}

kw("break")
kw("case", beforeExpr)
kw("catch")
kw("continue")
kw("debugger")
kw("default")
kw("do", {isLoop: true})
kw("else", beforeExpr)
kw("finally")
kw("for", {isLoop: true})
kw("function")
kw("if")
kw("return", beforeExpr)
kw("switch")
kw("throw", beforeExpr)
kw("try")
kw("var")
kw("let")
kw("const")
kw("while", {isLoop: true})
kw("with")
kw("new", {beforeExpr: true, startsExpr: true})
kw("this", startsExpr)
kw("super", startsExpr)
kw("class")
kw("extends", beforeExpr)
kw("export")
kw("import")
kw("yield", {beforeExpr: true, startsExpr: true})
kw("null", startsExpr)
kw("true", startsExpr)
kw("false", startsExpr)
kw("in", {beforeExpr: true, binop: 7})
kw("instanceof", {beforeExpr: true, binop: 7})
kw("typeof", {beforeExpr: true, prefix: true, startsExpr: true})
kw("void", {beforeExpr: true, prefix: true, startsExpr: true})
kw("delete", {beforeExpr: true, prefix: true, startsExpr: true})
<commit_msg>Mark function token as able to start an expression<commit_after>// ## Token types

// The assignment of fine-grained, information-carrying type objects
// allows the tokenizer to store the information it has about a
// token in a way that is very cheap for the parser to look up.

// All token type variables start with an underscore, to make them
// easy to recognize.

// The `beforeExpr` property is used to disambiguate between regular
// expressions and divisions. It is set on all token types that can
// be followed by an expression (thus, a slash after them would be a
// regular expression).
//
// `isLoop` marks a keyword as starting a loop, which is important
// to know when parsing a label, in order to allow or disallow
// continue jumps to that label.

export class TokenType {
  constructor(label, conf = {}) {
    this.label = label
    this.keyword = conf.keyword
    this.beforeExpr = !!conf.beforeExpr
    this.startsExpr = !!conf.startsExpr
    this.rightAssociative = !!conf.rightAssociative
    this.isLoop = !!conf.isLoop
    this.isAssign = !!conf.isAssign
    this.prefix = !!conf.prefix
    this.postfix = !!conf.postfix
    this.binop = conf.binop || null
    this.updateContext = null
  }
}

function binop(name, prec) {
  return new TokenType(name, {beforeExpr: true, binop: prec})
}
const beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true}

export const types = {
  num: new TokenType("num", startsExpr),
  regexp: new TokenType("regexp", startsExpr),
  string: new TokenType("string", startsExpr),
  name: new TokenType("name", startsExpr),
  eof: new TokenType("eof"),

  // Punctuation token types.
  bracketL: new TokenType("[", {beforeExpr: true, startsExpr: true}),
  bracketR: new TokenType("]"),
  braceL: new TokenType("{", {beforeExpr: true, startsExpr: true}),
  braceR: new TokenType("}"),
  parenL: new TokenType("(", {beforeExpr: true, startsExpr: true}),
  parenR: new TokenType(")"),
  comma: new TokenType(",", beforeExpr),
  semi: new TokenType(";", beforeExpr),
  colon: new TokenType(":", beforeExpr),
  dot: new TokenType("."),
  question: new TokenType("?", beforeExpr),
  arrow: new TokenType("=>", beforeExpr),
  template: new TokenType("template"),
  ellipsis: new TokenType("...", beforeExpr),
  backQuote: new TokenType("`", startsExpr),
  dollarBraceL: new TokenType("${", {beforeExpr: true, startsExpr: true}),
  at: new TokenType("@"),

  // Operators. These carry several kinds of properties to help the
  // parser use them properly (the presence of these properties is
  // what categorizes them as operators).
  //
  // `binop`, when present, specifies that this operator is a binary
  // operator, and will refer to its precedence.
  //
  // `prefix` and `postfix` mark the operator as a prefix or postfix
  // unary operator.
  //
  // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
  // binary operators with a very low precedence, that should result
  // in AssignmentExpression nodes.

  eq: new TokenType("=", {beforeExpr: true, isAssign: true}),
  assign: new TokenType("_=", {beforeExpr: true, isAssign: true}),
  incDec: new TokenType("++/--", {prefix: true, postfix: true, startsExpr: true}),
  prefix: new TokenType("prefix", {beforeExpr: true, prefix: true, startsExpr: true}),
  logicalOR: binop("||", 1),
  logicalAND: binop("&&", 2),
  bitwiseOR: binop("|", 3),
  bitwiseXOR: binop("^", 4),
  bitwiseAND: binop("&", 5),
  equality: binop("==/!=", 6),
  relational: binop("</>", 7),
  bitShift: binop("<</>>", 8),
  plusMin: new TokenType("+/-", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),
  modulo: binop("%", 10),
  star: binop("*", 10),
  slash: binop("/", 10),
  exponent: new TokenType("**", {beforeExpr: true, binop: 11, rightAssociative: true})
}

// Map keyword names to token types.

export const keywords = {}

// Succinct definitions of keyword token types
function kw(name, options = {}) {
  options.keyword = name
  keywords[name] = types["_" + name] = new TokenType(name, options)
}

kw("break")
kw("case", beforeExpr)
kw("catch")
kw("continue")
kw("debugger")
kw("default")
kw("do", {isLoop: true})
kw("else", beforeExpr)
kw("finally")
kw("for", {isLoop: true})
kw("function", startsExpr)
kw("if")
kw("return", beforeExpr)
kw("switch")
kw("throw", beforeExpr)
kw("try")
kw("var")
kw("let")
kw("const")
kw("while", {isLoop: true})
kw("with")
kw("new", {beforeExpr: true, startsExpr: true})
kw("this", startsExpr)
kw("super", startsExpr)
kw("class")
kw("extends", beforeExpr)
kw("export")
kw("import")
kw("yield", {beforeExpr: true, startsExpr: true})
kw("null", startsExpr)
kw("true", startsExpr)
kw("false", startsExpr)
kw("in", {beforeExpr: true, binop: 7})
kw("instanceof", {beforeExpr: true, binop: 7})
kw("typeof", {beforeExpr: true, prefix: true, startsExpr: true})
kw("void", {beforeExpr: true, prefix: true, startsExpr: true})
kw("delete", {beforeExpr: true, prefix: true, startsExpr: true})
<|endoftext|>


<commit_before>/**
 * Amata.js - List multi-selection multi-action form multi-task executer
 *
 * @link    https://github.com/textpattern/amata
 * @license MIT
 */

/*
 * Copyright (C) 2013 The Textpattern Development Team
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @name jQuery
 * @class
 */

/**
 * @name fn
 * @class
 * @memberOf jQuery
 */

(function (factory)
{
    'use strict';

    if (typeof define === 'function' && define.amd)
    {
        define(['jquery'], factory);
    }
    else
    {
        factory(window.jQuery);
    }
}(function ($)
{
    'use strict';

    /**
     * Methods.
     *
     * @var {Object}
     */

    var methods = {};

    /**
     * Initializes the multi-editor.
     *
     * @param {Object} [options={}] Options
     */

    methods.init = function (options)
    {
        options = $.extend({
            'selectAll'     : '[name=amata-select-all]',
            'button'        : '[type=submit]',
            'boxes'         : '[name="selected[]"]',
            'highlighted'   : 'tr',
            'selectedClass' : 'highlighted',
            'clickRegion'   : 'table tbody tr td'
        }, options);

        this.closest('form').not('.amata-ready').each(function ()
        {
            var $this = $(this), button = $this.find(options.button).hide(), actions = $(this).find('select.amata-actions').val(''), selectAll = $(options.selectAll), lastCheck = false;

            methods.registerOptions.call($this);

            selectAll.on('change', function ()
            {
                methods.select.call($this, {
                    'checked' : $(this).prop('checked')
                });
            });

            $this.on('click.amata', options.clickRegion, function (e)
            {
                var $this = $(this), self = ($(e.target).is(options.boxes) || $(this).is(options.boxes)), box, checked;

                if (!self)
                {
                    // Clicking the row, but without holding CTRL/ALT.

                    if (!e.altKey && !e.ctrlKey)
                    {
                        return;
                    }

                    // Skip actions invoked from clicking links and inputs.

                    if (e.target !== this || $this.is('a, :input') || $(e.target).is('a, :input'))
                    {
                        return;
                    }
                }

                box = $this.closest(options.highlighted).find(options.boxes).eq(0);

                if (!box.length)
                {
                    return;
                }

                checked = box.prop('checked');

                if (self)
                {
                    checked = !checked;
                }

                if (e.shiftKey && lastCheck)
                {
                    var boxes = $this.find(options.boxes);
                    var start = boxes.index(box);
                    var end = boxes.index(lastCheck);

                    methods.select({
                        'range'   : [Math.min(start, end), Math.max(start, end) + 1],
                        'checked' : !checked
                    });
                }
                else if (!self)
                {
                    box.prop('checked', !checked).change();
                }

                if (checked === false)
                {
                    lastCheck = box;
                }
                else
                {
                    lastCheck = null;
                }
            });

            $this.on('change.amata', options.boxes, function ()
            {
                var box = $(this), boxes = $(options.boxes);

                if (box.prop('checked'))
                {
                    box.closest(options.highlighted).addClass(options.selectedClass);
                    selectAll.prop('checked', boxes.filter(':checked').length === boxes.length);
                }
                else
                {
                    box.closest(options.highlighted).removeClass(options.selectedClass);
                    selectAll.prop('checked', false);
                }
            });

            actions.on('change.amata', function ()
            {
                var selected = $(this).find('option:selected').eq(0);
                $this.trigger('amata-action-change.amata').find('.amata-active-step').remove();

                if (!selected.val())
                {
                    button.hide();
                    return;
                }

                if (selected.data('amata-method'))
                {
                    $(this).after($('<div />').attr('class', 'amata-active-step').html(selected.data('amata-method')));
                    button.show();
                }
                else
                {
                    button.hide();
                    $this.trigger('submit.amata');
                }
            });

            $this.addClass('.amata-ready');
        });

        return this;
    };

    /**
     * Adds a multi-edit option.
     *
     * @param  {object} options
     * @param  {string} options.label The option's label
     * @param  {string} options.value The option's value
     * @param  {string} options.html  The second step HTML
     * @return {object} this
     * @example
     * $('form').amata('addOption', {
     *  'label' : 'Delete',
     *  'value' : 'delete'
     * });
     */

    methods.addOption = function (options)
    {
        options = $.extend({
            'label' : null,
            'value' : null,
            'html'  : null
        }, options);

        var $this = this.closest('form').find('.amata-actions').eq(0).val(''), exists = false;

        if (!options.value || !$this.length)
        {
            return this;
        }

        var option = $this.find('option').filter(function ()
        {
            return $(this).val() === options.value;
        }).eq(0);

        exists = (option.length !== 0);

        if (!exists)
        {
            option = $('<option />');
        }

        if (!exists || !option.data('amata-method'))
        {
            if (!option.val())
            {
                option.val(options.value);
            }

            if (!option.text() && options.label)
            {
                option.text(options.label);
            }

            option.data('amata-method', options.html);

            if (!exists)
            {
                $this.append(option);
            }
        }

        return this;
    };

    /**
     * Registers multi-edit options from the document.
     *
     * @return   {Object} this
     * @method   registerOptions
     * @memberof jQuery.fn.amata
     */

    methods.registerOptions = function ()
    {
        return this.each(function ()
        {
            var $this = $(this), multiOptions = $this.find('[data-amata-option]');

            $this.find('select.amata-actions option').each(function ()
            {
                var value = $(this).val();

                if (value)
                {
                    var option = multiOptions.filter(function ()
                    {
                        return $(this).attr('data-amata-option') === value;
                    });

                    if (option.length > 0)
                    {
                        methods.addOption.call($this, {
                            'label' : null,
                            'html'  : option.eq(0).contents(),
                            'value' : $(this).val()
                        });
                    }
                }
            });

            multiOptions.remove();
        });
    };

    /**
     * Selects rows based on supplied arguments.
     *
     * Only one of the filters applies at a time.
     *
     * @param  {object}  options
     * @param  {array}   options.index   Indexes to select
     * @param  {array}   options.range   Select index range, takes [min, max]
     * @param  {array}   options.value   Values to select
     * @param  {boolean} options.checked TRUE to check, FALSE to uncheck
     * @return {object}  methods
     */

    methods.select = function (options)
    {
        options = $.extend({
            'index'   : null,
            'range'   : null,
            'value'   : null,
            'checked' : true
        }, options);

        return this.each(function ()
        {
            var obj = $(this).find('[name="selected[]"]');

            if (options.value !== null)
            {
                obj = obj.filter(function ()
                {
                    return $.inArray($(this).val(), options.value) !== -1;
                });
            }
            else if (options.index !== null)
            {
                obj = obj.filter(function (index)
                {
                    return $.inArray(index, options.index) !== -1;
                });
            }
            else if (options.range !== null)
            {
                obj = obj.slice(options.range[0], options.range[1]);
            }

            obj.prop('checked', options.checked).change();
        });
    };

    /**
     * Multi-edit functions.
     *
     * @param    {String|Object} [method=init] Called method
     * @param    {Object}        [options={}]  Options passed to the method
     * @class    amata
     * @memberof jQuery.fn
     */

    $.fn.amata = function (method, options)
    {
        if ($.type(method) !== 'string' || $.type(methods[method]) !== 'function')
        {
            options = method;
            method = 'init';
        }

        return methods[method].call(this, options);
    };
}));<commit_msg>Dropped select method.<commit_after>/**
 * Amata.js - List multi-selection multi-action form multi-task executer
 *
 * @link    https://github.com/textpattern/amata
 * @license MIT
 */

/*
 * Copyright (C) 2013 The Textpattern Development Team
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @name jQuery
 * @class
 */

/**
 * @name fn
 * @class
 * @memberOf jQuery
 */

(function (factory)
{
    'use strict';

    if (typeof define === 'function' && define.amd)
    {
        define(['jquery'], factory);
    }
    else
    {
        factory(window.jQuery);
    }
}(function ($)
{
    'use strict';

    /**
     * Methods.
     *
     * @var {Object}
     */

    var methods = {};

    /**
     * Initializes the multi-editor.
     *
     * @param {Object} [options={}] Options
     */

    methods.init = function (options)
    {
        options = $.extend({
            'selectAll'     : '[name=amata-select-all]',
            'button'        : '[type=submit]',
            'boxes'         : '[name="selected[]"]',
            'highlighted'   : 'tr',
            'selectedClass' : 'highlighted',
            'clickRegion'   : 'table tbody tr td'
        }, options);

        this.closest('form').not('.amata-ready').each(function ()
        {
            var $this = $(this), button = $this.find(options.button).hide(), actions = $(this).find('select.amata-actions').val(''), selectAll = $(options.selectAll), lastCheck = false;

            methods.registerOptions.call($this);

            selectAll.on('change', function ()
            {
                $(options.boxes).prop('checked', $(this).prop('checked')).change();
            });

            $this.on('click.amata', options.clickRegion, function (e)
            {
                var $this = $(this), self = ($(e.target).is(options.boxes) || $(this).is(options.boxes)), box, checked;

                if (!self)
                {
                    // Clicking the row, but without holding CTRL/ALT.

                    if (!e.altKey && !e.ctrlKey)
                    {
                        return;
                    }

                    // Skip actions invoked from clicking links and inputs.

                    if (e.target !== this || $this.is('a, :input') || $(e.target).is('a, :input'))
                    {
                        return;
                    }
                }

                box = $this.closest(options.highlighted).find(options.boxes).eq(0);

                if (!box.length)
                {
                    return;
                }

                checked = box.prop('checked');

                if (self)
                {
                    checked = !checked;
                }

                if (e.shiftKey && lastCheck)
                {
                    var boxes = $this.find(options.boxes), start = boxes.index(box), end = boxes.index(lastCheck);

                    boxes
                        .slice(Math.min(start, end), Math.max(start, end) + 1)
                        .prop('checked', !checked)
                        .change();
                }
                else if (!self)
                {
                    box.prop('checked', !checked).change();
                }

                if (checked === false)
                {
                    lastCheck = box;
                }
                else
                {
                    lastCheck = null;
                }
            });

            $this.on('change.amata', options.boxes, function ()
            {
                var box = $(this), boxes = $(options.boxes);

                if (box.prop('checked'))
                {
                    box.closest(options.highlighted).addClass(options.selectedClass);
                    selectAll.prop('checked', boxes.filter(':checked').length === boxes.length);
                }
                else
                {
                    box.closest(options.highlighted).removeClass(options.selectedClass);
                    selectAll.prop('checked', false);
                }
            });

            actions.on('change.amata', function ()
            {
                var selected = $(this).find('option:selected').eq(0);
                $this.trigger('amata-action-change.amata').find('.amata-active-step').remove();

                if (!selected.val())
                {
                    button.hide();
                    return;
                }

                if (selected.data('amata-method'))
                {
                    $(this).after($('<div />').attr('class', 'amata-active-step').html(selected.data('amata-method')));
                    button.show();
                }
                else
                {
                    button.hide();
                    $this.trigger('submit.amata');
                }
            });

            $this.addClass('.amata-ready');
        });

        return this;
    };

    /**
     * Adds a multi-edit option.
     *
     * @param  {object} options
     * @param  {string} options.label The option's label
     * @param  {string} options.value The option's value
     * @param  {string} options.html  The second step HTML
     * @return {object} this
     * @example
     * $('form').amata('addOption', {
     *  'label' : 'Delete',
     *  'value' : 'delete'
     * });
     */

    methods.addOption = function (options)
    {
        options = $.extend({
            'label' : null,
            'value' : null,
            'html'  : null
        }, options);

        var $this = this.closest('form').find('.amata-actions').eq(0).val(''), exists = false;

        if (!options.value || !$this.length)
        {
            return this;
        }

        var option = $this.find('option').filter(function ()
        {
            return $(this).val() === options.value;
        }).eq(0);

        exists = (option.length !== 0);

        if (!exists)
        {
            option = $('<option />');
        }

        if (!exists || !option.data('amata-method'))
        {
            if (!option.val())
            {
                option.val(options.value);
            }

            if (!option.text() && options.label)
            {
                option.text(options.label);
            }

            option.data('amata-method', options.html);

            if (!exists)
            {
                $this.append(option);
            }
        }

        return this;
    };

    /**
     * Registers multi-edit options from the document.
     *
     * @return   {Object} this
     * @method   registerOptions
     * @memberof jQuery.fn.amata
     */

    methods.registerOptions = function ()
    {
        return this.each(function ()
        {
            var $this = $(this), multiOptions = $this.find('[data-amata-option]');

            $this.find('select.amata-actions option').each(function ()
            {
                var value = $(this).val();

                if (value)
                {
                    var option = multiOptions.filter(function ()
                    {
                        return $(this).attr('data-amata-option') === value;
                    });

                    if (option.length > 0)
                    {
                        methods.addOption.call($this, {
                            'label' : null,
                            'html'  : option.eq(0).contents(),
                            'value' : $(this).val()
                        });
                    }
                }
            });

            multiOptions.remove();
        });
    };

    /**
     * Multi-edit functions.
     *
     * @param    {String|Object} [method=init] Called method
     * @param    {Object}        [options={}]  Options passed to the method
     * @class    amata
     * @memberof jQuery.fn
     */

    $.fn.amata = function (method, options)
    {
        if ($.type(method) !== 'string' || $.type(methods[method]) !== 'function')
        {
            options = method;
            method = 'init';
        }

        return methods[method].call(this, options);
    };
}));<|endoftext|>


<commit_before>/*******************************************************************************
 * $URL:  $
 * $Id:  $
 * **********************************************************************************
 *
 * Copyright (c) 2010 The Sakai Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

//http://www.quirksmode.org/js/findpos.html
function findPos(obj) {
    var curleft = curtop = 0;
    if (obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return [curleft,curtop];
    }
}
var sakai = sakai || {};
sakai.editor = sakai.editor || {};
sakai.editor.editors = sakai.editor.editors || {};
// Temporarily disable enableResourceSearch till citations plugin is ported (SAK-22862)
sakai.editor.enableResourceSearch = false;

sakai.editor.editors.ckeditor = {};
// Please note that no more parameters should be added to this signature.
// The config object allows for name-based config options to be passed.
// The w and h parameters should be removed as soon as their uses can be migrated.
sakai.editor.editors.ckeditor.launch = function(targetId, config, w, h) {
    var folder = "";

    var collectionId = "";
    if (config != null && config.collectionId) {
        collectionId=config.collectionId;
    }
    else if (sakai.editor.collectionId) {
        collectionId=sakai.editor.collectionId
    }

    if (collectionId) {
        folder = "CurrentFolder=" + collectionId
    }

    var imageType = "Image";
    if (config && config.encodedImage) {
	imageType = "EncodedImage";
    }

    var language = sakai.locale && sakai.locale.userLanguage || '';
    var country = sakai.locale && sakai.locale.userCountry || null;

    var ckconfig = {
	//Some defaults for audio recorder
        audiorecorder : {
            "maxSeconds" : 180,
            "attemptAllowed" : Number.MAX_VALUE,
            "attemptsRemaining": Number.MAX_VALUE
        },
        skin: 'moono',
        defaultLanguage: 'en',
        allowedContent: true, // http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter-section-3
        language: language + (country ? '-' + country.toLowerCase() : ''),
        height: 310,
        fileConnectorUrl : '/sakai-fck-connector/web/editor/filemanager/browser/default/connectors/jsp/connector' + collectionId + '?' + folder,

        filebrowserBrowseUrl :'/library/editor/FCKeditor/editor/filemanager/browser/default/browser.html?Connector=/sakai-fck-connector/web/editor/filemanager/browser/default/connectors/jsp/connector' + collectionId + '&' + folder,
        filebrowserImageBrowseUrl : '/library/editor/FCKeditor/editor/filemanager/browser/default/browser.html?Type=Image&Connector=/sakai-fck-connector/web/editor/filemanager/browser/default/connectors/jsp/connector' + collectionId + '&' + folder,
        filebrowserFlashBrowseUrl :'/library/editor/FCKeditor/editor/filemanager/browser/default/browser.html?Type=Flash&Connector=/sakai-fck-connector/web/editor/filemanager/browser/default/connectors/jsp/connector' + collectionId + '&' + folder,
				extraPlugins: (sakai.editor.enableResourceSearch ? 'resourcesearch,' : '')+'',

        // These two settings enable the browser's native spell checking and context menus.
        // Control-Right-Click (Windows/Linux) or Command-Right-Click (Mac) on highlighted words
        // will cause the CKEditor menu to be suppressed and display the browser's standard context
        // menu. In some cases (Firefox and Safari, at least), this supplies corrections, suggestions, etc.
        disableNativeSpellChecker: false,
        browserContextMenuOnCtrl: true,
        entities_additional: '',
        removePlugins: 'scayt,menubutton,contextmenu',

        toolbar_Basic:
        [
            ['Source', '-', 'Bold', 'Italic', 'Link', 'Unlink']
        ],
        toolbar_Full:
        [
            ['Source','-','Templates'],
            // Uncomment the next line and comment the following to enable the default spell checker.
            // Note that it uses spellchecker.net, displays ads and sends content to remote servers without additional setup.
            //['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'],
            ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print'],
            ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
            ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'],
            '/',
            ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],
						['atd-ckeditor'],
            ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
            ['BidiLtr', 'BidiRtl' ],
            ['Link','Unlink','Anchor'],
            (sakai.editor.enableResourceSearch
                ? ['AudioRecorder','ResourceSearch',imageType,'Movie',/*'Flash',*/'Table','HorizontalRule','Smiley','SpecialChar','fmath_formula']
                : ['AudioRecorder',imageType,'Movie',/*'Flash',*/'Table','HorizontalRule','Smiley','SpecialChar','fmath_formula']),
            '/',
            ['Styles','Format','Font','FontSize'],
            ['TextColor','BGColor'],
            ['Maximize', 'ShowBlocks'],
            ['Audio', 'kalturaflash', 'magicembed', 'Youtube'],
            ['ckeditor_wiris_formulaEditor', 'ckeditor_wiris_CAS']
        ],
        toolbar: 'Full',
        resize_dir: 'both',
        //SAK-23418
        pasteFromWordRemoveFontStyles : false,
        pasteFromWordRemoveStyles : false
    };

    //NOTE: The height and width properties are handled discretely here.
    //      The ultimate intent is that the caller-supplied config will simply
    //      overlay the default config. The outstanding question is whether
    //      some properties should disallow override (because of specific setup
    //      here that we would not want duplicated throughout calling code) or
    //      if their override would just be discouraged. We also probably want
    //      some symbolic things like editorSize: 'small', where the supplied
    //      values are interpreted and translated into dimensions, toolbar set,
    //      and anything else relevant. This will allow editor indifference
    //      on the part of tool code, requesting whatever editor be launched
    //      with appropriate settings applied, rather than detecting the editor
    //      and supplying specific values for the desired effect. This set of
    //      "logical" configuration options is yet to be determined.
    if (config) {
        if (config.width) {
            ckconfig.width = config.width;
        } else if (w) {
            ckconfig.width = w;
        }

        if (config.height) {
            ckconfig.height = config.height;
        } else if (h) {
            ckconfig.height = h;
        }

        if (config && config.toolbarSet && ckconfig['toolbar_' + config.toolbarSet]) {
            ckconfig.toolbar = config.toolbarSet;
		}

		if (config.audiorecorder) {
			ckconfig.audiorecorder = config.audiorecorder;
		}

		if (config.disableBrowseServer)
		{
			ckconfig.filebrowserBrowseUrl = null;
			ckconfig.filebrowserImageBrowseUrl = null;
			ckconfig.filebrowserFlashBrowseUrl = null;
			ckconfig.filebrowserLinkBrowseUrl = null;
		}
	}

		//get path of directory ckeditor
		//
		var basePath = CKEDITOR.basePath;
		basePath = basePath.substr(0, basePath.indexOf("ckeditor/"))+"ckextraplugins/";
		//To add extra plugins outside the plugins directory, add them here! (And in the variable)
		(function() {
		   CKEDITOR.plugins.addExternal('movieplayer',basePath+'movieplayer/', 'plugin.js');
		   CKEDITOR.plugins.addExternal('wordcount',basePath+'wordcount/', 'plugin.js');
		   CKEDITOR.plugins.addExternal('fmath_formula',basePath+'fmath_formula/', 'plugin.js');
		   CKEDITOR.plugins.addExternal('audiorecorder',basePath+'audiorecorder/', 'plugin.js');
 		   CKEDITOR.plugins.addExternal('audio',basePath+'audio/', 'plugin.js');
 		   CKEDITOR.plugins.addExternal('kalturaflash',basePath+'kalturaflash/', 'plugin.js');
 		   CKEDITOR.plugins.addExternal('magicembed',basePath+'magicembed/', 'plugin.js');
 		   CKEDITOR.plugins.addExternal('youtube',basePath+'youtube/', 'plugin.js');
 		   CKEDITOR.plugins.addExternal('ckeditor_wiris',basePath+'ckeditor_wiris/', 'plugin.js');
			 /*
			  To enable after the deadline uncomment these two lines and add atd-ckeditor to toolbar
			  and to extraPlugins. This also needs extra stylesheets.
			  See readme for more info http://www.polishmywriting.com/atd-ckeditor/readme.html
			  You have to actually setup a server or get an API key
			  Hopefully this will get easier to configure soon.
			 */
			 CKEDITOR.plugins.addExternal('atd-ckeditor',basePath+'atd-ckeditor/', 'plugin.js');
			 /*
			 Replace this with your own server if you download it from http://openatd.wordpress.com/
			 Or you can proxy to the public one, see the page for more information.
			 */
			 //ckconfig.atd_rpc='//localhost/proxy/spellcheck';
			 //ckconfig.extraPlugins+="atd-ckeditor,";
			 //ckconfig.contentsCss = basePath+'/atd-ckeditor/atd.css';

			 ckconfig.extraPlugins+="encodedimage,audiorecorder,movieplayer,wordcount,fmath_formula,audio,kalturaflash,magicembed,youtube,ckeditor_wiris";
    })();

	  CKEDITOR.replace(targetId, ckconfig);
      //SAK-22505
      CKEDITOR.on('dialogDefinition', function(e) {
          // Take the dialog name and its definition from the event
          // data.
          var dialogName = e.data.name;
          var dialogDefinition = e.data.definition;

          var onShow = dialogDefinition.onShow;
          dialogDefinition.onShow = function() {
              var pos = findPos(e.editor.container.$);
              this.move(this.getPosition().x, pos[1]);
              if (typeof onShow !== 'undefined' && typeof onShow.call === 'function') {
                  var result = onShow.call(this);
                  return result;
              }
          }

          if ( dialogName == 'link' )
          {
              var targetTab = dialogDefinition.getContents('target');
              var linkTypeItems = targetTab.elements[0].children[0].items;
              var itemsNoPopup = [];
              for (i=0;i<linkTypeItems.length;i++) {
                  if (linkTypeItems[i][1] != "popup") {
                      itemsNoPopup.push(linkTypeItems[i]);
                  }
              }
              targetTab.elements[0].children[0].items = itemsNoPopup;

          }

          if ( dialogName == 'link' )
          {
              var targetTab = dialogDefinition.getContents('target');
              var linkTypeItems = targetTab.elements[0].children[0].items;
              var itemsNoPopup = [];
              for (i=0;i<linkTypeItems.length;i++) {
                  if (linkTypeItems[i][1] != "popup") {
                      itemsNoPopup.push(linkTypeItems[i]);
                  }
              }
              targetTab.elements[0].children[0].items = itemsNoPopup;

          }

      });
}

sakai.editor.launch = sakai.editor.editors.ckeditor.launch;

<commit_msg>CLASSES-1355 Disable language, liststyle and tabletools plugins to turn allow us to turn off the context menu<commit_after>/*******************************************************************************
 * $URL:  $
 * $Id:  $
 * **********************************************************************************
 *
 * Copyright (c) 2010 The Sakai Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

//http://www.quirksmode.org/js/findpos.html
function findPos(obj) {
    var curleft = curtop = 0;
    if (obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return [curleft,curtop];
    }
}
var sakai = sakai || {};
sakai.editor = sakai.editor || {};
sakai.editor.editors = sakai.editor.editors || {};
// Temporarily disable enableResourceSearch till citations plugin is ported (SAK-22862)
sakai.editor.enableResourceSearch = false;

sakai.editor.editors.ckeditor = {};
// Please note that no more parameters should be added to this signature.
// The config object allows for name-based config options to be passed.
// The w and h parameters should be removed as soon as their uses can be migrated.
sakai.editor.editors.ckeditor.launch = function(targetId, config, w, h) {
    var folder = "";

    var collectionId = "";
    if (config != null && config.collectionId) {
        collectionId=config.collectionId;
    }
    else if (sakai.editor.collectionId) {
        collectionId=sakai.editor.collectionId
    }

    if (collectionId) {
        folder = "CurrentFolder=" + collectionId
    }

    var imageType = "Image";
    if (config && config.encodedImage) {
	imageType = "EncodedImage";
    }

    var language = sakai.locale && sakai.locale.userLanguage || '';
    var country = sakai.locale && sakai.locale.userCountry || null;

    var ckconfig = {
	//Some defaults for audio recorder
        audiorecorder : {
            "maxSeconds" : 180,
            "attemptAllowed" : Number.MAX_VALUE,
            "attemptsRemaining": Number.MAX_VALUE
        },
        skin: 'moono',
        defaultLanguage: 'en',
        allowedContent: true, // http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter-section-3
        language: language + (country ? '-' + country.toLowerCase() : ''),
        height: 310,
        fileConnectorUrl : '/sakai-fck-connector/web/editor/filemanager/browser/default/connectors/jsp/connector' + collectionId + '?' + folder,

        filebrowserBrowseUrl :'/library/editor/FCKeditor/editor/filemanager/browser/default/browser.html?Connector=/sakai-fck-connector/web/editor/filemanager/browser/default/connectors/jsp/connector' + collectionId + '&' + folder,
        filebrowserImageBrowseUrl : '/library/editor/FCKeditor/editor/filemanager/browser/default/browser.html?Type=Image&Connector=/sakai-fck-connector/web/editor/filemanager/browser/default/connectors/jsp/connector' + collectionId + '&' + folder,
        filebrowserFlashBrowseUrl :'/library/editor/FCKeditor/editor/filemanager/browser/default/browser.html?Type=Flash&Connector=/sakai-fck-connector/web/editor/filemanager/browser/default/connectors/jsp/connector' + collectionId + '&' + folder,
				extraPlugins: (sakai.editor.enableResourceSearch ? 'resourcesearch,' : '')+'',

        // These two settings enable the browser's native spell checking and context menus.
        // Control-Right-Click (Windows/Linux) or Command-Right-Click (Mac) on highlighted words
        // will cause the CKEditor menu to be suppressed and display the browser's standard context
        // menu. In some cases (Firefox and Safari, at least), this supplies corrections, suggestions, etc.
        disableNativeSpellChecker: false,
        browserContextMenuOnCtrl: true,
        entities_additional: '',
        removePlugins: 'language,liststyle,tabletools,scayt,menubutton,contextmenu',

        toolbar_Basic:
        [
            ['Source', '-', 'Bold', 'Italic', 'Link', 'Unlink']
        ],
        toolbar_Full:
        [
            ['Source','-','Templates'],
            // Uncomment the next line and comment the following to enable the default spell checker.
            // Note that it uses spellchecker.net, displays ads and sends content to remote servers without additional setup.
            //['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'],
            ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print'],
            ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
            ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'],
            '/',
            ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],
						['atd-ckeditor'],
            ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
            ['BidiLtr', 'BidiRtl' ],
            ['Link','Unlink','Anchor'],
            (sakai.editor.enableResourceSearch
                ? ['AudioRecorder','ResourceSearch',imageType,'Movie',/*'Flash',*/'Table','HorizontalRule','Smiley','SpecialChar','fmath_formula']
                : ['AudioRecorder',imageType,'Movie',/*'Flash',*/'Table','HorizontalRule','Smiley','SpecialChar','fmath_formula']),
            '/',
            ['Styles','Format','Font','FontSize'],
            ['TextColor','BGColor'],
            ['Maximize', 'ShowBlocks'],
            ['Audio', 'kalturaflash', 'magicembed', 'Youtube'],
            ['ckeditor_wiris_formulaEditor', 'ckeditor_wiris_CAS']
        ],
        toolbar: 'Full',
        resize_dir: 'both',
        //SAK-23418
        pasteFromWordRemoveFontStyles : false,
        pasteFromWordRemoveStyles : false
    };

    //NOTE: The height and width properties are handled discretely here.
    //      The ultimate intent is that the caller-supplied config will simply
    //      overlay the default config. The outstanding question is whether
    //      some properties should disallow override (because of specific setup
    //      here that we would not want duplicated throughout calling code) or
    //      if their override would just be discouraged. We also probably want
    //      some symbolic things like editorSize: 'small', where the supplied
    //      values are interpreted and translated into dimensions, toolbar set,
    //      and anything else relevant. This will allow editor indifference
    //      on the part of tool code, requesting whatever editor be launched
    //      with appropriate settings applied, rather than detecting the editor
    //      and supplying specific values for the desired effect. This set of
    //      "logical" configuration options is yet to be determined.
    if (config) {
        if (config.width) {
            ckconfig.width = config.width;
        } else if (w) {
            ckconfig.width = w;
        }

        if (config.height) {
            ckconfig.height = config.height;
        } else if (h) {
            ckconfig.height = h;
        }

        if (config && config.toolbarSet && ckconfig['toolbar_' + config.toolbarSet]) {
            ckconfig.toolbar = config.toolbarSet;
		}

		if (config.audiorecorder) {
			ckconfig.audiorecorder = config.audiorecorder;
		}

		if (config.disableBrowseServer)
		{
			ckconfig.filebrowserBrowseUrl = null;
			ckconfig.filebrowserImageBrowseUrl = null;
			ckconfig.filebrowserFlashBrowseUrl = null;
			ckconfig.filebrowserLinkBrowseUrl = null;
		}
	}

		//get path of directory ckeditor
		//
		var basePath = CKEDITOR.basePath;
		basePath = basePath.substr(0, basePath.indexOf("ckeditor/"))+"ckextraplugins/";
		//To add extra plugins outside the plugins directory, add them here! (And in the variable)
		(function() {
		   CKEDITOR.plugins.addExternal('movieplayer',basePath+'movieplayer/', 'plugin.js');
		   CKEDITOR.plugins.addExternal('wordcount',basePath+'wordcount/', 'plugin.js');
		   CKEDITOR.plugins.addExternal('fmath_formula',basePath+'fmath_formula/', 'plugin.js');
		   CKEDITOR.plugins.addExternal('audiorecorder',basePath+'audiorecorder/', 'plugin.js');
 		   CKEDITOR.plugins.addExternal('audio',basePath+'audio/', 'plugin.js');
 		   CKEDITOR.plugins.addExternal('kalturaflash',basePath+'kalturaflash/', 'plugin.js');
 		   CKEDITOR.plugins.addExternal('magicembed',basePath+'magicembed/', 'plugin.js');
 		   CKEDITOR.plugins.addExternal('youtube',basePath+'youtube/', 'plugin.js');
 		   CKEDITOR.plugins.addExternal('ckeditor_wiris',basePath+'ckeditor_wiris/', 'plugin.js');
			 /*
			  To enable after the deadline uncomment these two lines and add atd-ckeditor to toolbar
			  and to extraPlugins. This also needs extra stylesheets.
			  See readme for more info http://www.polishmywriting.com/atd-ckeditor/readme.html
			  You have to actually setup a server or get an API key
			  Hopefully this will get easier to configure soon.
			 */
			 CKEDITOR.plugins.addExternal('atd-ckeditor',basePath+'atd-ckeditor/', 'plugin.js');
			 /*
			 Replace this with your own server if you download it from http://openatd.wordpress.com/
			 Or you can proxy to the public one, see the page for more information.
			 */
			 //ckconfig.atd_rpc='//localhost/proxy/spellcheck';
			 //ckconfig.extraPlugins+="atd-ckeditor,";
			 //ckconfig.contentsCss = basePath+'/atd-ckeditor/atd.css';

			 ckconfig.extraPlugins+="encodedimage,audiorecorder,movieplayer,wordcount,fmath_formula,audio,kalturaflash,magicembed,youtube,ckeditor_wiris";
    })();

	  CKEDITOR.replace(targetId, ckconfig);
      //SAK-22505
      CKEDITOR.on('dialogDefinition', function(e) {
          // Take the dialog name and its definition from the event
          // data.
          var dialogName = e.data.name;
          var dialogDefinition = e.data.definition;

          var onShow = dialogDefinition.onShow;
          dialogDefinition.onShow = function() {
              var pos = findPos(e.editor.container.$);
              this.move(this.getPosition().x, pos[1]);
              if (typeof onShow !== 'undefined' && typeof onShow.call === 'function') {
                  var result = onShow.call(this);
                  return result;
              }
          }

          if ( dialogName == 'link' )
          {
              var targetTab = dialogDefinition.getContents('target');
              var linkTypeItems = targetTab.elements[0].children[0].items;
              var itemsNoPopup = [];
              for (i=0;i<linkTypeItems.length;i++) {
                  if (linkTypeItems[i][1] != "popup") {
                      itemsNoPopup.push(linkTypeItems[i]);
                  }
              }
              targetTab.elements[0].children[0].items = itemsNoPopup;

          }

          if ( dialogName == 'link' )
          {
              var targetTab = dialogDefinition.getContents('target');
              var linkTypeItems = targetTab.elements[0].children[0].items;
              var itemsNoPopup = [];
              for (i=0;i<linkTypeItems.length;i++) {
                  if (linkTypeItems[i][1] != "popup") {
                      itemsNoPopup.push(linkTypeItems[i]);
                  }
              }
              targetTab.elements[0].children[0].items = itemsNoPopup;

          }

      });
}

sakai.editor.launch = sakai.editor.editors.ckeditor.launch;

<|endoftext|>