<commit_before>/**
 * concrete5 wrapper for jQuery UI
 */
$.widget("concrete.dialog", $.ui.dialog, {
    _allowInteraction: function(event) {
        return !!$( event.target).closest('.ccm-interaction-dialog').length || this._super(event);
    }
});

$.widget.bridge( "jqdialog", $.concrete.dialog );
// wrap our old dialog function in the new dialog() function.
jQuery.fn.dialog = function() {
    // Pass this over to jQuery UI Dialog in a few circumstances
    if (arguments.length > 0) {
        $(this).jqdialog(arguments[0], arguments[1], arguments[2]);
        return;
    } else if ($(this).is('div')) {
        $(this).jqdialog();
        return;
    }
    // LEGACY SUPPORT
    return $(this).each(function() {
        $(this).unbind('click.make-dialog').bind('click.make-dialog', function(e) {
            var href = $(this).attr('href');
            var width = $(this).attr('dialog-width');
            var height =$(this).attr('dialog-height');
            var title = $(this).attr('dialog-title');
            var onOpen = $(this).attr('dialog-on-open');
            var onDestroy = $(this).attr('dialog-on-destroy');
            /*
             * no longer necessary. we auto detect
                var appendButtons = $(this).attr('dialog-append-buttons');
            */
            var onClose = $(this).attr('dialog-on-close');
            var onDirectClose = $(this).attr('dialog-on-direct-close');
            obj = {
                modal: true,
                href: href,
                width: width,
                height: height,
                title: title,
                onOpen: onOpen,
                onDestroy: onDestroy,
                onClose: onClose,
                onDirectClose: onDirectClose
            }
            jQuery.fn.dialog.open(obj);
            return false;
        });
    });
}

jQuery.fn.dialog.close = function(num) {
    num++;
    $("#ccm-dialog-content" + num).jqdialog('close');
}

jQuery.fn.dialog.open = function(options) {
    if (typeof(ConcreteMenu) != 'undefined') {
        var activeMenu = ConcreteMenuManager.getActiveMenu();
        if (activeMenu) {
            activeMenu.hide();
        }
    }

    if (typeof(options.width) == 'string') {
        if (options.width == 'auto') {
            w = 'auto';
        } else {
            if (options.width.indexOf('%', 0) > 0) {
                w = options.width.replace('%', '');
                w = $(window).width() * (w / 100);
                w = w + 50;
            } else {
                w = parseInt(options.width) + 50;
            }
        }
    } else if (options.width) {
        w = parseInt(options.width) + 50;
    } else {
        w = 550;
    }

    if (typeof(options.height) == 'string') {
        if (options.height == 'auto') {
            h = 'auto';
        } else {
            if (options.height.indexOf('%', 0) > 0) {
                h = options.height.replace('%', '');
                h = $(window).height() * (h / 100);
                h = h + 100;
            } else {
                h = parseInt(options.height) + 100;
            }
        }
    } else if (options.height) {
        h = parseInt(options.height) + 100;
    } else {
        h = 400;
    }
    if (h !== 'auto' && h > $(window).height()) {
        h = $(window).height();
    }

    options.width = w;
    options.height = h;

    var defaults = {
        'modal': true,
        'escapeClose': true,
        'width': w,
        'height': h,
        'resizable': true,

        'create': function() {
            $(this).parent().addClass('animated fadeIn');
        },

        'open': function() {
            var $dialog = $(this);
            var nd = $(".ui-dialog").length;
            if (nd == 1) {
                $("body").attr('data-last-overflow', $('body').css('overflow'));
                $("body").css("overflow", "hidden");
            }
            var overlays = $('.ui-widget-overlay').length;
            $('.ui-widget-overlay').each(function(i, obj) {
                if ((i + 1) < overlays) {
                    $(this).css('opacity', 0);
                }
            });

            jQuery.fn.dialog.activateDialogContents($dialog);

            // on some brother (eg: Chrome) the resizable get hidden because the button pane
            // in on top of it, here is a fix for this:
            if ( $dialog.jqdialog('option', 'resizable') )
            {
                var $wrapper = $($dialog.parent());
                var z = parseInt($wrapper.find('.ui-dialog-buttonpane').css('z-index'));
                $wrapper.find('.ui-resizable-handle').css('z-index', z + 1000);
            }

            if (typeof options.onOpen != "undefined") {
                if ((typeof options.onOpen) == 'function') {
                    options.onOpen($dialog);
                } else {
                    eval(options.onOpen);
                }
            }

        },
        'beforeClose': function() {
            var nd = $(".ui-dialog").length;
            if (nd == 1) {
                $("body").css("overflow", $('body').attr('data-last-overflow'));
            }
        },
        'close': function(ev, u) {
            if (!options.element) {
                $(this).jqdialog('destroy').remove();
            }
            if (typeof options.onClose != "undefined") {
                if ((typeof options.onClose) == 'function') {
                    options.onClose($(this));
                } else {
                    eval(options.onClose);
                }
            }
            if (typeof options.onDirectClose != "undefined" && ev.handleObj && (ev.handleObj.type == 'keydown' || ev.handleObj.type == 'click')) {
                if ((typeof options.onDirectClose) == 'function') {
                    options.onDirectClose();
                } else {
                    eval(options.onDirectClose);
                }
            }
            if (typeof options.onDestroy != "undefined") {
                if ((typeof options.onDestroy) == 'function') {
                    options.onDestroy();
                } else {
                    eval(options.onDestroy);
                }
            }
            var overlays = $('.ui-widget-overlay').length;
            $('.ui-widget-overlay').each(function(i, obj) {
                if ((i + 1) < overlays) {
                    $(this).css('opacity', 0);
                } else {
                    $(this).css('opacity', 1);
                }
            });
        }
    };

    var finalSettings = {'autoOpen': false, 'data': {} };
    $.extend(finalSettings, defaults, options);

    if (finalSettings.element) {
        $(finalSettings.element).jqdialog(finalSettings).jqdialog();
        $(finalSettings.element).jqdialog('open');
    } else {
        jQuery.fn.dialog.showLoader();
        $.ajax({
            type: 'GET',
            url: finalSettings.href,
            data: finalSettings.data,
            success: function(r) {
                jQuery.fn.dialog.hideLoader();
                // note the order here is very important in order to actually run javascript in
                // the pages we load while having access to the jqdialog object.
                // Ensure that the dialog is open prior to evaluating javascript.
                $('
').jqdialog(finalSettings).html(r).jqdialog('open');
            }
        });
    }

}

jQuery.fn.dialog.activateDialogContents = function($dialog) {
    // handle buttons
    $dialog.find('button[data-dialog-action=cancel]').on('click', function() {
        jQuery.fn.dialog.closeTop();
    });
    $('[data-dialog-form]').concreteAjaxForm();

    $dialog.find('button[data-dialog-action=submit]').on('click', function() {
        $('[data-dialog-form]').submit();
    });

    if ($dialog.find('.dialog-buttons').length > 0) {
        $dialog.jqdialog('option', 'buttons', [{}]);
        $dialog.parent().find(".ui-dialog-buttonset").remove();
        $dialog.parent().find(".ui-dialog-buttonpane").html('');
        $dialog.find('.dialog-buttons').removeClass().appendTo($dialog.parent().find('.ui-dialog-buttonpane').addClass("ccm-ui"));
    }

    // make dialogs
    $dialog.find('.dialog-launch').dialog();

    // automated close handling
    $dialog.find('.ccm-dialog-close').on('click', function() {
        $dialog.dialog('close');
    });

    $dialog.find('.launch-tooltip').tooltip({'container': '#ccm-tooltip-holder'});

    // help handling
    if ($dialog.find('.dialog-help').length > 0) {
        $dialog.find('.dialog-help').hide();
        var helpContent = $dialog.find('.dialog-help').html();
        if (ccmi18n.helpPopup) {
            var helpText = ccmi18n.helpPopup;
        } else {
            var helpText = 'Help';
        }
        var button = $('<button class="ui-dialog-titlebar-help ccm-menu-help-trigger"></button>'),
            container = $('#ccm-tooltip-holder');
        $dialog.parent().find('.ui-dialog-titlebar').addClass('ccm-ui').append(button);
        button.popover({
            content: function() {
                return helpContent;
            },
            placement: 'bottom',
            html: true,
            container: container,
            trigger: 'click'
        });
        button.on('shown.bs.popover', function() {
            var binding = function() {
                button.popover('hide', button);
                binding = $.noop;
            };

            button.on('hide.bs.popover', function(event) {
                button.unbind(event);
                binding = $.noop;
            });

            $('body').mousedown(function(e) {
                if ($(e.target).closest(container).length || $(e.target).closest(button).length) {
                    return;
                }
                $(this).unbind(e);
                binding();
            });
        });
    }
}

jQuery.fn.dialog.getTop = function() {
    var nd = $(".ui-dialog:visible").length;
    return $($('.ui-dialog:visible')[nd-1]).find('.ui-dialog-content');
}

jQuery.fn.dialog.replaceTop = function(html) {
    $dialog = jQuery.fn.dialog.getTop();
    $dialog.html(html);
    jQuery.fn.dialog.activateDialogContents($dialog);
}

jQuery.fn.dialog.showLoader = function(text) {
    $('body').addClass('ccm-loading');
}

jQuery.fn.dialog.hideLoader = function() {
    $('body').removeClass('ccm-loading');
}

jQuery.fn.dialog.closeTop = function() {
    $dialog = jQuery.fn.dialog.getTop();
    $dialog.jqdialog('close');
}

jQuery.fn.dialog.closeAll = function() {
    $($(".ui-dialog-content").get().reverse()).jqdialog('close');
}

<commit_msg>Fixing bug with some blocks disabling scrolling once dialogs are closed<commit_after>/**
 * concrete5 wrapper for jQuery UI
 */
$.widget("concrete.dialog", $.ui.dialog, {
    _allowInteraction: function(event) {
        return !!$( event.target).closest('.ccm-interaction-dialog').length || this._super(event);
    }
});

$.widget.bridge( "jqdialog", $.concrete.dialog );
// wrap our old dialog function in the new dialog() function.
jQuery.fn.dialog = function() {
    // Pass this over to jQuery UI Dialog in a few circumstances
    if (arguments.length > 0) {
        $(this).jqdialog(arguments[0], arguments[1], arguments[2]);
        return;
    } else if ($(this).is('div')) {
        $(this).jqdialog();
        return;
    }
    // LEGACY SUPPORT
    return $(this).each(function() {
        $(this).unbind('click.make-dialog').bind('click.make-dialog', function(e) {
            var href = $(this).attr('href');
            var width = $(this).attr('dialog-width');
            var height =$(this).attr('dialog-height');
            var title = $(this).attr('dialog-title');
            var onOpen = $(this).attr('dialog-on-open');
            var onDestroy = $(this).attr('dialog-on-destroy');
            /*
             * no longer necessary. we auto detect
                var appendButtons = $(this).attr('dialog-append-buttons');
            */
            var onClose = $(this).attr('dialog-on-close');
            var onDirectClose = $(this).attr('dialog-on-direct-close');
            obj = {
                modal: true,
                href: href,
                width: width,
                height: height,
                title: title,
                onOpen: onOpen,
                onDestroy: onDestroy,
                onClose: onClose,
                onDirectClose: onDirectClose
            }
            jQuery.fn.dialog.open(obj);
            return false;
        });
    });
}

jQuery.fn.dialog.close = function(num) {
    num++;
    $("#ccm-dialog-content" + num).jqdialog('close');
}

jQuery.fn.dialog.open = function(options) {
    if (typeof(ConcreteMenu) != 'undefined') {
        var activeMenu = ConcreteMenuManager.getActiveMenu();
        if (activeMenu) {
            activeMenu.hide();
        }
    }

    if (typeof(options.width) == 'string') {
        if (options.width == 'auto') {
            w = 'auto';
        } else {
            if (options.width.indexOf('%', 0) > 0) {
                w = options.width.replace('%', '');
                w = $(window).width() * (w / 100);
                w = w + 50;
            } else {
                w = parseInt(options.width) + 50;
            }
        }
    } else if (options.width) {
        w = parseInt(options.width) + 50;
    } else {
        w = 550;
    }

    if (typeof(options.height) == 'string') {
        if (options.height == 'auto') {
            h = 'auto';
        } else {
            if (options.height.indexOf('%', 0) > 0) {
                h = options.height.replace('%', '');
                h = $(window).height() * (h / 100);
                h = h + 100;
            } else {
                h = parseInt(options.height) + 100;
            }
        }
    } else if (options.height) {
        h = parseInt(options.height) + 100;
    } else {
        h = 400;
    }
    if (h !== 'auto' && h > $(window).height()) {
        h = $(window).height();
    }

    options.width = w;
    options.height = h;

    var defaults = {
        'modal': true,
        'escapeClose': true,
        'width': w,
        'height': h,
        'resizable': true,

        'create': function() {
            $(this).parent().addClass('animated fadeIn');
        },

        'open': function() {
            var $dialog = $(this);
            var nd = $(".ui-dialog").length;
            if (nd == 1) {
                $("body").attr('data-last-overflow', $('body').css('overflow'));
                $("body").css("overflow", "hidden");
            }
            var overlays = $('.ui-widget-overlay').length;
            $('.ui-widget-overlay').each(function(i, obj) {
                if ((i + 1) < overlays) {
                    $(this).css('opacity', 0);
                }
            });

            jQuery.fn.dialog.activateDialogContents($dialog);

            // on some brother (eg: Chrome) the resizable get hidden because the button pane
            // in on top of it, here is a fix for this:
            if ( $dialog.jqdialog('option', 'resizable') )
            {
                var $wrapper = $($dialog.parent());
                var z = parseInt($wrapper.find('.ui-dialog-buttonpane').css('z-index'));
                $wrapper.find('.ui-resizable-handle').css('z-index', z + 1000);
            }

            if (typeof options.onOpen != "undefined") {
                if ((typeof options.onOpen) == 'function') {
                    options.onOpen($dialog);
                } else {
                    eval(options.onOpen);
                }
            }

        },
        'beforeClose': function() {
            var nd = $(".ui-dialog:visible").length;
            if (nd == 1) {
                $("body").css("overflow", $('body').attr('data-last-overflow'));
            }
        },
        'close': function(ev, u) {
            if (!options.element) {
                $(this).jqdialog('destroy').remove();
            }
            if (typeof options.onClose != "undefined") {
                if ((typeof options.onClose) == 'function') {
                    options.onClose($(this));
                } else {
                    eval(options.onClose);
                }
            }
            if (typeof options.onDirectClose != "undefined" && ev.handleObj && (ev.handleObj.type == 'keydown' || ev.handleObj.type == 'click')) {
                if ((typeof options.onDirectClose) == 'function') {
                    options.onDirectClose();
                } else {
                    eval(options.onDirectClose);
                }
            }
            if (typeof options.onDestroy != "undefined") {
                if ((typeof options.onDestroy) == 'function') {
                    options.onDestroy();
                } else {
                    eval(options.onDestroy);
                }
            }
            var overlays = $('.ui-widget-overlay').length;
            $('.ui-widget-overlay').each(function(i, obj) {
                if ((i + 1) < overlays) {
                    $(this).css('opacity', 0);
                } else {
                    $(this).css('opacity', 1);
                }
            });
        }
    };

    var finalSettings = {'autoOpen': false, 'data': {} };
    $.extend(finalSettings, defaults, options);

    if (finalSettings.element) {
        $(finalSettings.element).jqdialog(finalSettings).jqdialog();
        $(finalSettings.element).jqdialog('open');
    } else {
        jQuery.fn.dialog.showLoader();
        $.ajax({
            type: 'GET',
            url: finalSettings.href,
            data: finalSettings.data,
            success: function(r) {
                jQuery.fn.dialog.hideLoader();
                // note the order here is very important in order to actually run javascript in
                // the pages we load while having access to the jqdialog object.
                // Ensure that the dialog is open prior to evaluating javascript.
                $('
').jqdialog(finalSettings).html(r).jqdialog('open');
            }
        });
    }

}

jQuery.fn.dialog.activateDialogContents = function($dialog) {
    // handle buttons
    $dialog.find('button[data-dialog-action=cancel]').on('click', function() {
        jQuery.fn.dialog.closeTop();
    });
    $('[data-dialog-form]').concreteAjaxForm();

    $dialog.find('button[data-dialog-action=submit]').on('click', function() {
        $('[data-dialog-form]').submit();
    });

    if ($dialog.find('.dialog-buttons').length > 0) {
        $dialog.jqdialog('option', 'buttons', [{}]);
        $dialog.parent().find(".ui-dialog-buttonset").remove();
        $dialog.parent().find(".ui-dialog-buttonpane").html('');
        $dialog.find('.dialog-buttons').removeClass().appendTo($dialog.parent().find('.ui-dialog-buttonpane').addClass("ccm-ui"));
    }

    // make dialogs
    $dialog.find('.dialog-launch').dialog();

    // automated close handling
    $dialog.find('.ccm-dialog-close').on('click', function() {
        $dialog.dialog('close');
    });

    $dialog.find('.launch-tooltip').tooltip({'container': '#ccm-tooltip-holder'});

    // help handling
    if ($dialog.find('.dialog-help').length > 0) {
        $dialog.find('.dialog-help').hide();
        var helpContent = $dialog.find('.dialog-help').html();
        if (ccmi18n.helpPopup) {
            var helpText = ccmi18n.helpPopup;
        } else {
            var helpText = 'Help';
        }
        var button = $('<button class="ui-dialog-titlebar-help ccm-menu-help-trigger"></button>'),
            container = $('#ccm-tooltip-holder');
        $dialog.parent().find('.ui-dialog-titlebar').addClass('ccm-ui').append(button);
        button.popover({
            content: function() {
                return helpContent;
            },
            placement: 'bottom',
            html: true,
            container: container,
            trigger: 'click'
        });
        button.on('shown.bs.popover', function() {
            var binding = function() {
                button.popover('hide', button);
                binding = $.noop;
            };

            button.on('hide.bs.popover', function(event) {
                button.unbind(event);
                binding = $.noop;
            });

            $('body').mousedown(function(e) {
                if ($(e.target).closest(container).length || $(e.target).closest(button).length) {
                    return;
                }
                $(this).unbind(e);
                binding();
            });
        });
    }
}

jQuery.fn.dialog.getTop = function() {
    var nd = $(".ui-dialog:visible").length;
    return $($('.ui-dialog:visible')[nd-1]).find('.ui-dialog-content');
}

jQuery.fn.dialog.replaceTop = function(html) {
    $dialog = jQuery.fn.dialog.getTop();
    $dialog.html(html);
    jQuery.fn.dialog.activateDialogContents($dialog);
}

jQuery.fn.dialog.showLoader = function(text) {
    $('body').addClass('ccm-loading');
}

jQuery.fn.dialog.hideLoader = function() {
    $('body').removeClass('ccm-loading');
}

jQuery.fn.dialog.closeTop = function() {
    $dialog = jQuery.fn.dialog.getTop();
    $dialog.jqdialog('close');
}

jQuery.fn.dialog.closeAll = function() {
    $($(".ui-dialog-content").get().reverse()).jqdialog('close');
}

<|endoftext|>


<commit_before>'use strict'
const test = require('tape')
const Hub = require('../')

test('client - subscription', (t) => {
  const server = new Hub({
    port: 6000,
    id: 'server',
    content: { title: 'content' },
    nothing: { title: 'nothing' },
    x: { title: 'x' }
  })

  const client = new Hub({
    context: false,
    id: 'client',
    url: 'ws://localhost:6000'
  })

  const client2 = new Hub({
    context: false,
    id: 'client2',
    url: 'ws://localhost:6000'
  })

  const subs = {
    receiver: {
      $switch: {
        exec (state) {
          if (state && state.root.client) {
            return state.root.client.origin() === state.origin() ? 'receiver' : 'sender'
          }
        },
        sender: {
          $root: {
            nothing: {
              title: {
                val: true
              }
            }
          }
        },
        receiver: {
          $root: {
            content: {
              title: {
                val: true
              }
            }
          }
        }
      }
    }
  }

  client.subscribe(subs, (state) => {
    t.same(state.path(), [ 'content', 'title' ])
  })

  client2.subscribe(subs, (state) => {
    t.same(state.path(), [ 'nothing', 'title' ])
  })

  client.set({ receiver: client.client.origin() })
})
<commit_msg>added tests<commit_after>'use strict'
const test = require('tape')
const Hub = require('../')

test('client - subscription', (t) => {
  const server = new Hub({
    port: 6000,
    id: 'server',
    content: { title: 'content' },
    nothing: { title: 'nothing' },
    x: { title: 'x' }
  })

  const client = new Hub({
    context: false,
    id: 'client',
    url: 'ws://localhost:6000'
  })

  const client2 = new Hub({
    context: false,
    id: 'client2',
    url: 'ws://localhost:6000'
  })

  const subs = {
    receiver: {
      $switch: {
        exec (state) {
          if (state && state.root.client) {
            return state.root.client.origin() === state.origin() ? 'receiver' : 'sender'
          }
        },
        sender: {
          $root: {
            nothing: {
              title: {
                val: true
              }
            }
          }
        },
        receiver: {
          $root: {
            content: {
              title: {
                val: true
              }
            }
          }
        }
      }
    }
  }

  client.subscribe(subs, (state) => {
    t.same(state.path(), [ 'content', 'title' ])
  })

  client2.subscribe(subs, (state) => {
    t.same(state.path(), [ 'nothing', 'title' ])
  })

  client.set({ receiver: client.client.origin() })

  Promise.all([
    client.get('content.title', {}).is('content'),
    client2.get('nothing.title', {}).is('nothing')
  ]).then(() => {
    client.remove()
    client2.remove()
    server.remove()
    t.end()
  })
})
<|endoftext|>


<commit_before>var co = require('co')
var os = require('os')
var fs = require('fs')
var path = require('path')
var http = require('http')
var setup = require('proxy')

var request = require('..')

var tmpdir = os.tmpdir()
var uri = 'https://raw.github.com/component/domify/84b1917ea5a9451f5add48c5f61e477f2788532b/component.json'
var redirect = 'https://raw.github.com/jonathanong/inherits/master/component.json'

var server = setup(http.createServer())

describe('cogent', function () {
  it('should work with HTTPS', co(function* () {
    var res = yield* request(uri)
    res.statusCode.should.equal(200)
    res.headers['content-encoding'].should.equal('gzip')
    res.resume()
  }))

  it('should start proxy server', function (done) {
    server.listen(4205, done)
  })

  it('should work with HTTP proxy', co(function* () {
    var res = yield* request(uri, {
      proxy: 'http://localhost:4205'
    })
    res.statusCode.should.equal(200)
    res.headers['content-encoding'].should.equal('gzip')
    res.resume()
  }))

  it('should save to a file', co(function* () {
    var destination = path.join(tmpdir, Math.random().toString(36).slice(2))
    var res = yield* request(uri, destination)
    res.statusCode.should.equal(200)
    res.destination.should.equal(destination)
    fs.statSync(destination)
  }))

  it('should resolve redirects', co(function* () {
    var res = yield* request(redirect, true)
    res.urls.length.should.equal(2)
    res.statusCode.should.equal(200)
    res.body.name.should.equal('inherits')
  }))

  it('should work with retries', co(function* () {
    var res = yield* request(uri, {
      retries: 2
    })
    res.statusCode.should.equal(200)
    res.headers['content-encoding'].should.equal('gzip')
    res.resume()
  }))
})<commit_msg>add test<commit_after>var co = require('co')
var os = require('os')
var fs = require('fs')
var path = require('path')
var http = require('http')
var setup = require('proxy')

var request = require('..')

var tmpdir = os.tmpdir()
var uri = 'https://raw.github.com/component/domify/84b1917ea5a9451f5add48c5f61e477f2788532b/component.json'
var redirect = 'https://raw.github.com/jonathanong/inherits/master/component.json'

var server = setup(http.createServer())

describe('cogent', function () {
  it('should work with HTTPS', co(function* () {
    var res = yield* request(uri)
    res.statusCode.should.equal(200)
    res.headers['content-encoding'].should.equal('gzip')
    res.resume()
  }))

  it('should start proxy server', function (done) {
    server.listen(4205, done)
  })

  it('should work with HTTP proxy', co(function* () {
    var res = yield* request(uri, {
      proxy: 'http://localhost:4205'
    })
    res.statusCode.should.equal(200)
    res.headers['content-encoding'].should.equal('gzip')
    res.resume()
  }))

  it('should save to a file', co(function* () {
    var destination = path.join(tmpdir, Math.random().toString(36).slice(2))
    var res = yield* request(uri, destination)
    res.statusCode.should.equal(200)
    res.destination.should.equal(destination)
    fs.statSync(destination)
  }))

  it('should resolve redirects', co(function* () {
    var res = yield* request(redirect, true)
    res.urls.length.should.equal(2)
    res.statusCode.should.equal(200)
    res.body.name.should.equal('inherits')
  }))

  it('should not resolve redirects', co(function* () {
    var res = yield* request(redirect, {string: true, redirects: 0, method: 'GET'})
    res.statusCode.should.equal(301)
  }))

  it('should work with retries', co(function* () {
    var res = yield* request(uri, {
      retries: 2
    })
    res.statusCode.should.equal(200)
    res.headers['content-encoding'].should.equal('gzip')
    res.resume()
  }))
})
<|endoftext|>


<commit_before>import test from 'ava';
import { readFileSync } from 'fs';
import { removeSync } from 'fs-extra';
import { rollup } from 'rollup';
import sassJs from 'sass';
import sass from '..';

const sassOptions = {
  outputStyle: 'compressed',
};
const inputOptions = {
  plugins: [
    sass({
      options: sassOptions,
    }),
  ],
};
const outputOptions = {
  format: 'es',
};
const expectA = readFileSync('test/assets/expect_a.css').toString();
const expectB = readFileSync('test/assets/expect_b.css').toString();
const expectC = readFileSync('test/assets/expect_c.css').toString();
const expectD = readFileSync('test/assets/expect_d.css').toString();
const expectE = readFileSync('test/assets/expect_e.css').toString();

function squash(str) {
  return str.trim().replace(/[\n\r]/, '');
}

function reverse(str) {
  return str.split('').reverse().join('');
}

function unwrap(output) {
  return output[0].code;
}

test('should import *.scss and *.sass files', async t => {
  const bundle = await rollup({
    ...inputOptions,
    input: 'test/fixtures/basic/index.js',
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectB)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectC)) > -1);
});

test('should compress the dest CSS', async t => {
  const bundle = await rollup({
    ...inputOptions,
    input: 'test/fixtures/compress/index.js',
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectD)) > -1);
});

test('should custom importer running', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/custom-importer/index.js',
    plugins: [
      sass({
        options: {
          ...sassOptions,
          importer: [
            (url, prev, done) => {
              done({
                file: url.replace('${name}', 'actual_a'),
              });
            },
          ],
        }
      }),
    ]
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
});

test('should support options.data', async t => {
  const jsVars = {
    'color_red': 'red',
  };
  const scssVars = Object.keys(jsVars).reduce((prev, key) => `${prev}$${key}:${jsVars[key]};`, '');
  const bundle = await rollup({
    input: 'test/fixtures/data/index.js',
    plugins: [
      sass({
        options: {
          ...sassOptions,
          data: scssVars,
        },
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectE)) > -1);
});

test('should insert CSS into head tag', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/insert/index.js',
    plugins: [
      sass({
        insert: true,
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);
  let count = 0;

  global.window = {};
  global.document = {
    innerHTML: '',
    head: {
      appendChild (mockNode) {
        t.true(mockNode.hasOwnProperty('setAttribute'));

        if (count === 0) {
          t.is(squash(mockNode.innerHTML), squash(`${expectA}`));
        } else if (count === 1) {
          t.is(squash(mockNode.innerHTML), squash(`${expectB}`));
        }
        count += 1;
      },
    },
    createElement () {
      return {
        setAttribute (key, value) {
          if (key === 'type') {
            t.is(value, 'text/css');
          }
        },
      };
    },
  };
  new Function(unwrap(output))(); // eslint-disable-line
});

test('should support output as function', async t => {
  let outputCode = '';
  const bundle = await rollup({
    input: 'test/fixtures/output-function/index.js',
    plugins: [
      sass({
        output(style) {
          outputCode = style;
        },
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.is(squash(unwrap(output)), '');
  t.is(squash(outputCode), squash(`${expectA}${expectB}`));
});

test('should support output as (non-previously existent) path', async t => {
  const outputPath = 'test/fixtures/output-path/style.css';
  const bundle = await rollup({
    input: 'test/fixtures/output-path/index.js',
    plugins: [
      sass({
        output: outputPath,
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);
  const fsOutput = readFileSync(outputPath).toString();

  removeSync(outputPath);
  t.is(squash(unwrap(output)), '');
  t.is(squash(fsOutput), squash(`${expectA}${expectB}`));
});

test('should support output as true', async t => {
  const outputPath = 'test/fixtures/output-true/bundle.js';
  const bundle = await rollup({
    input: 'test/fixtures/output-true/index.js',
    plugins: [
      sass({
        output: true,
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate({
    ...outputOptions,
    file: outputPath,
  });
  const fsOutput = readFileSync(outputPath.replace('.js', '.css')).toString();

  removeSync(outputPath);
  t.is(squash(unwrap(output)), '');
  t.is(squash(fsOutput), squash(`${expectA}${expectB}`));
});

test('should processor return as string', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/processor-string/index.js',
    plugins: [
      sass({
        processor: css => reverse(css),
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(reverse(squash(expectA))) > -1);
  t.true(squash(unwrap(output)).indexOf(reverse(squash(expectB))) > -1);
});

test('should processor return as object', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/processor-object/index.js',
    plugins: [
      sass({
        processor(css) {
          return {
            css,
            foo: 'foo',
            bar: 'bar',
          };
        },
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectB)) > -1);
  t.true(squash(unwrap(output)).indexOf('foo') > -1);
  t.true(squash(unwrap(output)).indexOf('bar') > -1);
});

test('should processor return as promise', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/processor-promise/index.js',
    plugins: [
      sass({
        processor(css) {
          return new Promise(resolve => {
            setTimeout(() => {
              resolve(css);
            }, 100);
          });
        },
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectB)) > -1);
});

test('should processor throw error', async t => {
  await t.throws(async () => {
    await rollup({
      input: 'test/fixtures/processor-error/index.js',
      plugins: [
        sass({
          processor: code => ({}),
          options: sassOptions,
        }),
      ],
    });
  }, {
    message: 'You need to return the styles using the `css` property. See https://github.com/differui/rollup-plugin-sass#processor',
  });
});

test('should resolve ~ as node_modules', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/import/index.js',
    plugins: [
      sass({
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectB)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectC)) > -1);
});

test('should support options.runtime', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/runtime/index.js',
    plugins: [
      sass({
        runtime: sassJs,
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectB)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectC)) > -1);
});
<commit_msg>Fix CI error<commit_after>import test from 'ava';
import { readFileSync } from 'fs';
import { removeSync } from 'fs-extra';
import { rollup } from 'rollup';
import sassJs from 'sass';
import sass from '..';

const sassOptions = {
  outputStyle: 'compressed',
};
const inputOptions = {
  plugins: [
    sass({
      options: sassOptions,
    }),
  ],
};
const outputOptions = {
  format: 'es',
};
const expectA = readFileSync('test/assets/expect_a.css').toString();
const expectB = readFileSync('test/assets/expect_b.css').toString();
const expectC = readFileSync('test/assets/expect_c.css').toString();
const expectD = readFileSync('test/assets/expect_d.css').toString();
const expectE = readFileSync('test/assets/expect_e.css').toString();

function squash(str) {
  return str.trim().replace(/[\n\r]/, '');
}

function reverse(str) {
  return str.split('').reverse().join('');
}

function unwrap(output) {
  return output[0].code;
}

test('should import *.scss and *.sass files', async t => {
  const bundle = await rollup({
    ...inputOptions,
    input: 'test/fixtures/basic/index.js',
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectB)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectC)) > -1);
});

test('should compress the dest CSS', async t => {
  const bundle = await rollup({
    ...inputOptions,
    input: 'test/fixtures/compress/index.js',
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectD)) > -1);
});

test('should custom importer running', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/custom-importer/index.js',
    plugins: [
      sass({
        options: {
          ...sassOptions,
          importer: [
            (url, prev, done) => {
              done({
                file: url.replace('${name}', 'actual_a'),
              });
            },
          ],
        }
      }),
    ]
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
});

test('should support options.data', async t => {
  const jsVars = {
    'color_red': 'red',
  };
  const scssVars = Object.keys(jsVars).reduce((prev, key) => `${prev}$${key}:${jsVars[key]};`, '');
  const bundle = await rollup({
    input: 'test/fixtures/data/index.js',
    plugins: [
      sass({
        options: {
          ...sassOptions,
          data: scssVars,
        },
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectE)) > -1);
});

test('should insert CSS into head tag', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/insert/index.js',
    plugins: [
      sass({
        insert: true,
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);
  let count = 0;

  global.window = {};
  global.document = {
    innerHTML: '',
    head: {
      appendChild (mockNode) {
        t.true(mockNode.hasOwnProperty('setAttribute'));

        if (count === 0) {
          t.is(squash(mockNode.innerHTML), squash(`${expectA}`));
        } else if (count === 1) {
          t.is(squash(mockNode.innerHTML), squash(`${expectB}`));
        }
        count += 1;
      },
    },
    createElement () {
      return {
        setAttribute (key, value) {
          if (key === 'type') {
            t.is(value, 'text/css');
          }
        },
      };
    },
  };
  new Function(unwrap(output))(); // eslint-disable-line
});

test('should support output as function', async t => {
  let outputCode = '';
  const bundle = await rollup({
    input: 'test/fixtures/output-function/index.js',
    plugins: [
      sass({
        output(style) {
          outputCode = style;
        },
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.is(squash(unwrap(output)), '');
  t.is(squash(outputCode), squash(`${expectA}${expectB}`));
});

test('should support output as (non-previously existent) path', async t => {
  const outputPath = 'test/fixtures/output-path/style.css';
  const bundle = await rollup({
    input: 'test/fixtures/output-path/index.js',
    plugins: [
      sass({
        output: outputPath,
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);
  const fsOutput = readFileSync(outputPath).toString();

  removeSync(outputPath);
  t.is(squash(unwrap(output)), '');
  t.is(squash(fsOutput), squash(`${expectA}${expectB}`));
});

test('should support output as true', async t => {
  const outputPath = 'test/fixtures/output-true/bundle.js';
  const bundle = await rollup({
    input: 'test/fixtures/output-true/index.js',
    plugins: [
      sass({
        output: true,
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate({
    ...outputOptions,
    file: outputPath,
  });
  const fsOutput = readFileSync(outputPath.replace('.js', '.css')).toString();

  removeSync(outputPath);
  t.is(squash(unwrap(output)), '');
  t.is(squash(fsOutput), squash(`${expectA}${expectB}`));
});

test('should processor return as string', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/processor-string/index.js',
    plugins: [
      sass({
        processor: css => reverse(css),
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(reverse(squash(expectA))) > -1);
  t.true(squash(unwrap(output)).indexOf(reverse(squash(expectB))) > -1);
});

test('should processor return as object', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/processor-object/index.js',
    plugins: [
      sass({
        processor(css) {
          return {
            css,
            foo: 'foo',
            bar: 'bar',
          };
        },
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectB)) > -1);
  t.true(squash(unwrap(output)).indexOf('foo') > -1);
  t.true(squash(unwrap(output)).indexOf('bar') > -1);
});

test('should processor return as promise', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/processor-promise/index.js',
    plugins: [
      sass({
        processor(css) {
          return new Promise(resolve => {
            setTimeout(() => {
              resolve(css);
            }, 100);
          });
        },
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectB)) > -1);
});

test('should processor throw error', async t => {
  await t.throwsAsync(async () => {
    await rollup({
      input: 'test/fixtures/processor-error/index.js',
      plugins: [
        sass({
          processor: code => ({}),
          options: sassOptions,
        }),
      ],
    });
  }, {
    message: 'You need to return the styles using the `css` property. See https://github.com/differui/rollup-plugin-sass#processor',
  });
});

test('should resolve ~ as node_modules', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/import/index.js',
    plugins: [
      sass({
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectB)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectC)) > -1);
});

test('should support options.runtime', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/runtime/index.js',
    plugins: [
      sass({
        runtime: sassJs,
        options: sassOptions,
      }),
    ],
  });
  const { output } = await bundle.generate(outputOptions);

  t.true(squash(unwrap(output)).indexOf(squash(expectA)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectB)) > -1);
  t.true(squash(unwrap(output)).indexOf(squash(expectC)) > -1);
});
<|endoftext|>


<commit_before>var assert = require('assert')
var color = require('bash-color')
var Promise = require('promise')
var fs = require('../')

var failed = false
var ready = Promise.from(null)
function action(fn, message) {
  ready = ready.then(function () {
    return fn()
      .then(function () {
        console.info(color.green('V ') + message)
      }, function (ex) {
        console.error(color.red('X ') + message)
        console.error(ex.stack || ex.message || ex)
        failed = true
      })
  })
}

action(fs.mkdir.bind(null, __dirname + '/fixture'), 'mkdir')
action(function () {
  return fs.stat(__dirname + '/fixture')
          .then(function (stat) {
              assert(stat.isDirectory())
          })
}, 'stat directory')

action(fs.writeFile.bind(null, __dirname + '/fixture/file.txt', 'hello world'), 'writeFile')
action(function () {
  return fs.readFile(__dirname + '/fixture/file.txt', 'utf8')
          .then(function (txt) {
              assert(txt === 'hello world')
          })
}, 'readFile')
action(function () {
  return fs.readdir(__dirname + '/fixture')
          .then(function (files) {
            assert(Array.isArray(files))
            assert(files.length === 1)
            assert(files[0] === 'file.txt')
          })
}, 'readdir')
action(fs.unlink.bind(null, __dirname + '/fixture/file.txt'), 'unlink')

action(fs.rmdir.bind(null, __dirname + '/fixture'), 'rmdir')
action(function () {
  return fs.stat(__dirname + '/fixture')
          .then(function (stat) {
            throw new Error('directory still exists after rmdir')
          }, function () {})
}, 'stat directory')

ready.done(function () {
  if (failed) process.exit(1)
})<commit_msg>fix tests<commit_after>var assert = require('assert')
var color = require('bash-color')
var Promise = require('promise')
var fs = require('../')

var failed = false
var ready = Promise.resolve(null)
function action(fn, message) {
  ready = ready.then(function () {
    return fn()
      .then(function () {
        console.info(color.green('V ') + message)
      }, function (ex) {
        console.error(color.red('X ') + message)
        console.error(ex.stack || ex.message || ex)
        failed = true
      })
  })
}

action(fs.mkdir.bind(null, __dirname + '/fixture'), 'mkdir')
action(function () {
  return fs.stat(__dirname + '/fixture')
          .then(function (stat) {
              assert(stat.isDirectory())
          })
}, 'stat directory')

action(fs.writeFile.bind(null, __dirname + '/fixture/file.txt', 'hello world'), 'writeFile')
action(function () {
  return fs.readFile(__dirname + '/fixture/file.txt', 'utf8')
          .then(function (txt) {
              assert(txt === 'hello world')
          })
}, 'readFile')
action(function () {
  return fs.readdir(__dirname + '/fixture')
          .then(function (files) {
            assert(Array.isArray(files))
            assert(files.length === 1)
            assert(files[0] === 'file.txt')
          })
}, 'readdir')
action(fs.unlink.bind(null, __dirname + '/fixture/file.txt'), 'unlink')

action(fs.rmdir.bind(null, __dirname + '/fixture'), 'rmdir')
action(function () {
  return fs.stat(__dirname + '/fixture')
          .then(function (stat) {
            throw new Error('directory still exists after rmdir')
          }, function () {})
}, 'stat directory')

ready.done(function () {
  if (failed) process.exit(1)
})<|endoftext|>


<commit_before>
cdb.admin.BaseLayerButton = cdb.core.View.extend({

  bindMap: function(map) {
    map.bind('savingLayers', this.disable, this);
    map.bind('savingLayersFinish', this.enable, this);
    map.layers.bind('reset', this.checkIfSelected, this);
    map.layers.bind('add', this.checkIfSelected, this);
  },

  disable: function() {
    this.$el.addClass('disabled');
    this.undelegateEvents();
  },

  enable: function() {
    this.$el.removeClass('disabled');
    this.delegateEvents();
  },

  selectButton: function() {
    this.$el.parents('.base_maps').find('li').removeClass('selected');
    this.$el.addClass('selected');
  },

  /**
  * Check if the layer is the same than the map's baselayer and if so add the adequate class
  * @method checkIfSelected
  */
  checkIfSelected: function() {
    if(this.model.toJSON().options && this.options && this.options.map) {
      var baseLayer = this.options.map.getBaseLayer();
      if(baseLayer && this.model.isEqual(baseLayer)) {
        this.selectButton();
      }
    }
  }

});

/**
 * small photo of available base map layers
 */
cdb.admin.GMapsBaseView = cdb.admin.BaseLayerButton.extend({

  events: {
    'click': 'activate'
  },

  tagName: 'li',

  initialize: function() {
    this.options = _.defaults(this.options,this.defaults);
    this.map = this.options.map;

    this.bindMap(this.map);
  },

  render: function() {
    var a = this.make("a", {"class": "gmaps_" + this.model.get('base_type') }, this.cid);
    this.$el.html(a);
    this.elder('render');
    return this;
  },

  activate: function(e) {
    this.selectButton(); //probably it's still not "really" selected, but let's cheat on the user for UX
    e.preventDefault();
    this.map.changeProvider('googlemaps', this.model.clone());
    return false;
  }

});

cdb.admin.BaseMapView = cdb.admin.BaseLayerButton.extend({

  events: {
    'click span.remove_layer': '_openDropdown',
    'click': 'activate'
  },

  defaults: {
    // x,y,z position of the base tile preview
    x: 30,
    y: 24,
    z: 6
  },

  tagName: 'li',

  initialize: function() {
    this.options = _.defaults(this.options,this.defaults);
    this.map = this.options.map;
    this.model.bind('destroy', this.clean, this);
    this.bindMap(this.map);
  },

  render: function() {
    //TODO: move this to model
    var back_tile = this.model.get("urlTemplate").replace("{z}", this.options.z).replace("{x}", this.options.x).replace("{y}", this.options.y)
      , a = this.make("a", {"style": "background-image:url(" + back_tile + ");"}, this.cid);

    if(!this.model.get('read_only')) {
      del = this.make("span", {"class": "remove_layer"});
      $(a).append(del);

    }

    this.$el.html(a);
    this.$el.attr('data-tipsy', this.model.get("name"));
    this.$el.attr("title", this.model.get("name"));
    this.$el.tipsy({title: 'data-tipsy'})

    this.elder('render');
    return this;
  },

  activate: function(e) {
    e.preventDefault();
    this.selectButton(); //probably it's still not "really" selected, but let's cheat on the user for UX
    cdb.god.trigger("closeDialogs");
    // when the user selects a normal base layer select leaflet by default
    var lyr = this.model.clone();
    lyr.set('id', undefined); // force creation
    this.map.changeProvider('leaflet', lyr);
    return false;

  },

  _openDropdown: function(ev) {
    var self = this;

    ev.preventDefault();
    ev.stopPropagation();

    cdb.god.trigger("closeDialogs");

    this.dropdown = new cdb.admin.DropdownMenu({
      className: 'dropdown border',
      target: this.$('.remove_layer'),
      width: 196,
      speedIn: 150,
      speedOut: 300,
      template_base: 'table/views/remove_layer_content',
      vertical_position: "down",
      horizontal_position: "left",
      horizontal_offset: 3,
      clean_on_hide: true,
      tick: "left"
    }).bind("optionClicked", function(ev) {
      ev.preventDefault();
      self.model.destroy();
    });

    cdb.god.bind("closeDialogs", this.dropdown.hide, this.dropdown);

    $('body').append(this.dropdown.render().el);
    this.dropdown.open(ev);
  }

});

cdb.admin.BackgroundMapColorView = cdb.admin.BaseLayerButton.extend({

  events: {
    'click span.button' : '_openPicker',
    'click'             : '_openPicker'
  },

  tagName: 'li',
  className: 'map_background',

  initialize: function() {
    // this.options = _.defaults(this.options,this.defaults);
    this.map = this.model;
    this.current_color = '#FFFFFF';
    this.bindMap(this.map);
  },

  render: function() {
    this.$el.append("" +
      "#FFFFFF" + "");
    this.$el.attr('data-tipsy', 'click to change the map background color');
    this._initPicker();
    this.elder('render');
    return this;
  },

  activate: function(e) {
    var lyr = new cdb.admin.PlainLayer({
      color: this.current_color
    });

    this.map.changeProvider('leaflet', lyr);
    return false;
  },

  _initPicker: function() {
    var self = this;

    this.color_picker = new cdb.admin.ColorPicker({
      target: this.$el,
      template_base: 'common/views/color_picker',
      vertical_position: "down",
      horizontal_position: "left",
      horizontal_offset: 15,
      tick: "left"
    }).bind("colorChosen", function(color) {
      // Put new color
      self.$el.find("a").css({ "background": color })
      self.$el.find("span.color").text(color);

      // Set new model
      self.current_color = color;

    });
  },

  _openPicker: function(ev) {
    ev.preventDefault();
    ev.stopImmediatePropagation();

    cdb.god.unbind("closeDialogs", this.color_picker.hide, this.color_picker);

    cdb.god.trigger("closeDialogs");

    if (!this.color_picker.el.parentElement) {
      $('body').append(this.color_picker.render().el);
      this.color_picker.init("#FFFFFF");

      cdb.god.bind("closeDialogs", this.color_picker.hide, this.color_picker);
    } else {
      this.color_picker.hide();
    }
  }

});

cdb.admin.BaseMapChooser = cdb.core.View.extend({

  tagName: 'ul',

  events: {
    "click a.add" : "_openSelector"
  },

  initialize: function() {
    _.bindAll(this, 'add');
    this.baseLayers = this.options.baseLayers;
    this.baseLayers.bind('reset', this.render, this);
    this.baseLayers.bind('add', this.add, this);
  },

  _addBaseDefault: function() {
    this.baseLayers.each(this.add);
  },

  _addSelector: function() {
    var $li = $("

");
    this.$el.append($li);
    $li.attr('data-tipsy', 'click to add a new layer');
    $li.tipsy({title:"data-tipsy"});
  },

  _openSelector: function(ev) {
    var self = this;
    ev.preventDefault();
    var dialog = new cdb.admin.BaseMapAdder({
      model: this.model, //map
      baseLayers: this.baseLayers,
      ok: function(layer) {
        self.model.changeProvider('leaflet', layer.clone());
      }
    });
    dialog.appendToBody().open();

    return false;
  },

  add: function(lyr) {
    var v = new cdb.admin.BaseMapView({ model: lyr, map: this.model });
    cdb.log.debug("added base layer option: " + lyr.get('urlTemplate'));
    this.addView(v);

    if (this.$el.find("li.map_background").length > 0) {
      // Insert before the map background li
      $(v.render().el).insertBefore(this.$el.find("li.map_background"));
    } else {
      this.$el.append(v.render().el);
    }
  },

  _addGoogleMaps: function() {
    var
    available = ['roadmap', 'gray_roadmap', 'satellite'],
    names = {
      roadmap:      "Roadmap",
      satellite:    "Satellite",
      gray_roadmap: "Gray Roadmap"
    },
    styles = {
      roadmap: [],
      satellite: [],
      gray_roadmap: [ { stylers: [ { saturation: -65 }, { gamma: 1.52 } ] },{ featureType: "administrative", stylers: [ { saturation: -95 }, { gamma: 2.26 } ] },{ featureType: "water", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "administrative.locality", stylers: [ { visibility: "off" } ] },{ featureType: "road", stylers: [ { visibility: "simplified" }, { saturation: -99 }, { gamma: 2.22 } ] },{ featureType: "poi", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "road.arterial", stylers: [ { visibility: "off" } ] },{ featureType: "road.local", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "transit", stylers: [ { visibility: "off" } ] },{ featureType: "road", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "poi", stylers: [ { saturation: -55 } ] } ]
    };

    for(var i in available) {
      var layer_name = available[i];
      var base = new cdb.admin.GMapsBaseLayer({ base_type: layer_name, style: styles[layer_name], name: names[layer_name] });

      var v = new cdb.admin.GMapsBaseView({
        model: base,
        map: this.model
      });
      this.addView(v);
      var $view = $(v.render().el);
      $view.attr("title", names[layer_name]);

      this.$el.append($view);
    }

  },

  _addBackgroundView: function() {
    var v = new cdb.admin.BackgroundMapColorView({ model: this.model });
    cdb.log.debug("added background map color selector");
    this.addView(v);
    this.$el.append(v.render().el);
  },

  render: function() {
    this.$el.html('');
    // Draw default layers
    this._addBaseDefault();

    this._addGoogleMaps();
    // Add background map selector
    this._addBackgroundView();

    // Add tile button selector
    this._addSelector();

    this.$el.find('li').map(function(e,i) {
      var $li = $(i);
      if(!$li.attr('data-tipsy')) {
        $li.attr('data-tipsy', 'click to change the base layer');
        $li.tipsy({title:"data-tipsy"});
      }
    })

    this.$el.find('li.map_background')
      .attr('data-tipsy', 'click to change the background color')
      .tipsy({title:"data-tipsy"});

    this.cleanTooltips();

    return this;
  },

  /**
  * Checks every subview and mark as selected if necesary
  * @method markSelectedLayer
  */
  markSelectedLayer: function() {
    for(var cid in this._subviews) {
      this._subviews[cid].checkIfSelected();
    }
  }

});
<commit_msg>post basemap adding processing<commit_after>
cdb.admin.BaseLayerButton = cdb.core.View.extend({

  bindMap: function(map) {
    map.bind('savingLayers', this.disable, this);
    map.bind('savingLayersFinish', this.enable, this);
    map.layers.bind('reset', this.checkIfSelected, this);
    map.layers.bind('add', this.checkIfSelected, this);
  },

  disable: function() {
    this.$el.addClass('disabled');
    this.undelegateEvents();
  },

  enable: function() {
    this.$el.removeClass('disabled');
    this.delegateEvents();
  },

  selectButton: function() {
    this.$el.parents('.base_maps').find('li').removeClass('selected');
    this.$el.addClass('selected');
  },

  /**
  * Check if the layer is the same than the map's baselayer and if so add the adequate class
  * @method checkIfSelected
  */
  checkIfSelected: function() {
    if(this.model.toJSON().options && this.options && this.options.map) {
      var baseLayer = this.options.map.getBaseLayer();
      if(baseLayer && this.model.isEqual(baseLayer)) {
        this.selectButton();
      }
    }
  }

});

/**
 * small photo of available base map layers
 */
cdb.admin.GMapsBaseView = cdb.admin.BaseLayerButton.extend({

  events: {
    'click': 'activate'
  },

  tagName: 'li',

  initialize: function() {
    this.options = _.defaults(this.options,this.defaults);
    this.map = this.options.map;

    this.bindMap(this.map);
  },

  render: function() {
    var a = this.make("a", {"class": "gmaps_" + this.model.get('base_type') }, this.cid);
    this.$el.html(a);
    this.elder('render');
    return this;
  },

  activate: function(e) {
    this.selectButton(); //probably it's still not "really" selected, but let's cheat on the user for UX
    e.preventDefault();
    this.map.changeProvider('googlemaps', this.model.clone());
    return false;
  }

});

cdb.admin.BaseMapView = cdb.admin.BaseLayerButton.extend({

  events: {
    'click span.remove_layer': '_openDropdown',
    'click': 'activate'
  },

  defaults: {
    // x,y,z position of the base tile preview
    x: 30,
    y: 24,
    z: 6
  },

  tagName: 'li',

  initialize: function() {
    this.options = _.defaults(this.options,this.defaults);
    this.map = this.options.map;
    this.model.bind('destroy', this.clean, this);
    this.bindMap(this.map);
  },

  render: function() {
    //TODO: move this to model
    var back_tile = this.model.get("urlTemplate").replace("{z}", this.options.z).replace("{x}", this.options.x).replace("{y}", this.options.y)
      , a = this.make("a", {"style": "background-image:url(" + back_tile + ");"}, this.cid);

    if(!this.model.get('read_only')) {
      del = this.make("span", {"class": "remove_layer"});
      $(a).append(del);

    }

    this.$el.html(a);
    this.$el.attr('data-tipsy', this.model.get("name"));
    this.$el.attr("title", this.model.get("name"));
    this.$el.tipsy({title: 'data-tipsy'})

    this.elder('render');
    return this;
  },

  activate: function(e) {
    e.preventDefault();
    this.selectButton(); //probably it's still not "really" selected, but let's cheat on the user for UX
    cdb.god.trigger("closeDialogs");
    // when the user selects a normal base layer select leaflet by default
    var lyr = this.model.clone();
    lyr.set('id', undefined); // force creation
    this.map.changeProvider('leaflet', lyr);
    return false;

  },

  _openDropdown: function(ev) {
    var self = this;

    ev.preventDefault();
    ev.stopPropagation();

    cdb.god.trigger("closeDialogs");

    this.dropdown = new cdb.admin.DropdownMenu({
      className: 'dropdown border',
      target: this.$('.remove_layer'),
      width: 196,
      speedIn: 150,
      speedOut: 300,
      template_base: 'table/views/remove_layer_content',
      vertical_position: "down",
      horizontal_position: "left",
      horizontal_offset: 3,
      clean_on_hide: true,
      tick: "left"
    }).bind("optionClicked", function(ev) {
      ev.preventDefault();
      self.model.destroy();
    });

    cdb.god.bind("closeDialogs", this.dropdown.hide, this.dropdown);

    $('body').append(this.dropdown.render().el);
    this.dropdown.open(ev);
  }

});

cdb.admin.BackgroundMapColorView = cdb.admin.BaseLayerButton.extend({

  events: {
    'click span.button' : '_openPicker',
    'click'             : '_openPicker'
  },

  tagName: 'li',
  className: 'map_background',

  initialize: function() {
    // this.options = _.defaults(this.options,this.defaults);
    this.map = this.model;
    this.current_color = '#FFFFFF';
    this.bindMap(this.map);
  },

  render: function() {
    this.$el.append("" +
      "#FFFFFF" + "");
    this.$el.attr('data-tipsy', 'click to change the map background color');
    this._initPicker();
    this.elder('render');
    return this;
  },

  activate: function(e) {
    var lyr = new cdb.admin.PlainLayer({
      color: this.current_color
    });

    this.map.changeProvider('leaflet', lyr);
    return false;
  },

  _initPicker: function() {
    var self = this;

    this.color_picker = new cdb.admin.ColorPicker({
      target: this.$el,
      template_base: 'common/views/color_picker',
      vertical_position: "down",
      horizontal_position: "left",
      horizontal_offset: 15,
      tick: "left"
    }).bind("colorChosen", function(color) {
      // Put new color
      self.$el.find("a").css({ "background": color })
      self.$el.find("span.color").text(color);

      // Set new model
      self.current_color = color;

    });
  },

  _openPicker: function(ev) {
    ev.preventDefault();
    ev.stopImmediatePropagation();

    cdb.god.unbind("closeDialogs", this.color_picker.hide, this.color_picker);

    cdb.god.trigger("closeDialogs");

    if (!this.color_picker.el.parentElement) {
      $('body').append(this.color_picker.render().el);
      this.color_picker.init("#FFFFFF");

      cdb.god.bind("closeDialogs", this.color_picker.hide, this.color_picker);
    } else {
      this.color_picker.hide();
    }
  }

});

cdb.admin.BaseMapChooser = cdb.core.View.extend({

  tagName: 'ul',

  events: {
    "click a.add" : "_openSelector"
  },

  initialize: function() {
    _.bindAll(this, 'add');
    this.baseLayers = this.options.baseLayers;
    this.baseLayers.bind('reset', this.render, this);
    this.baseLayers.bind('add', this.add, this);
  },

  _addBaseDefault: function() {
    this.baseLayers.each(this.add);
  },

  _addSelector: function() {
    var $li = $("

");
    this.$el.append($li);
    $li.attr('data-tipsy', 'click to add a new layer');
    $li.tipsy({title:"data-tipsy"});
  },

  _openSelector: function(ev) {
    var self = this;
    ev.preventDefault();
    var dialog = new cdb.admin.BaseMapAdder({
      model: this.model, //map
      baseLayers: this.baseLayers,
      ok: function(layer) {
        self.model.changeProvider('leaflet', layer.clone());
      }
    });
    dialog.appendToBody().open();

    return false;
  },

  add: function(lyr) {
    var v = new cdb.admin.BaseMapView({ model: lyr, map: this.model });
    cdb.log.debug("added base layer option: " + lyr.get('urlTemplate'));
    this.addView(v);

    var element = v.render().el;
    if (this.$el.find("li.map_background").length > 0) {
      // Insert before the map background li
      $(element).insertBefore(this.$el.find("li.map_background"));
    } else {
      this.$el.append(element);
    }

    $(element).attr('data-tipsy', 'your basemap')
    $(element).tipsy();
  },

  _addGoogleMaps: function() {
    var
    available = ['roadmap', 'gray_roadmap', 'satellite'],
    names = {
      roadmap:      "Roadmap",
      satellite:    "Satellite",
      gray_roadmap: "Gray Roadmap"
    },
    styles = {
      roadmap: [],
      satellite: [],
      gray_roadmap: [ { stylers: [ { saturation: -65 }, { gamma: 1.52 } ] },{ featureType: "administrative", stylers: [ { saturation: -95 }, { gamma: 2.26 } ] },{ featureType: "water", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "administrative.locality", stylers: [ { visibility: "off" } ] },{ featureType: "road", stylers: [ { visibility: "simplified" }, { saturation: -99 }, { gamma: 2.22 } ] },{ featureType: "poi", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "road.arterial", stylers: [ { visibility: "off" } ] },{ featureType: "road.local", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "transit", stylers: [ { visibility: "off" } ] },{ featureType: "road", elementType: "labels", stylers: [ { visibility: "off" } ] },{ featureType: "poi", stylers: [ { saturation: -55 } ] } ]
    };

    for(var i in available) {
      var layer_name = available[i];
      var base = new cdb.admin.GMapsBaseLayer({ base_type: layer_name, style: styles[layer_name], name: names[layer_name] });

      var v = new cdb.admin.GMapsBaseView({
        model: base,
        map: this.model
      });
      this.addView(v);
      var $view = $(v.render().el);
      $view.attr("title", names[layer_name]);

      this.$el.append($view);
    }

  },

  _addBackgroundView: function() {
    var v = new cdb.admin.BackgroundMapColorView({ model: this.model });
    cdb.log.debug("added background map color selector");
    this.addView(v);
    this.$el.append(v.render().el);
  },

  render: function() {
    this.$el.html('');
    // Draw default layers
    this._addBaseDefault();

    this._addGoogleMaps();
    // Add background map selector
    this._addBackgroundView();

    // Add tile button selector
    this._addSelector();

    this.$el.find('li').map(function(e,i) {
      var $li = $(i);
      if(!$li.attr('data-tipsy')) {
        $li.attr('data-tipsy', 'click to change the base layer');
        $li.tipsy({title:"data-tipsy"});
      }
    })

    this.$el.find('li.map_background')
      .attr('data-tipsy', 'click to change the background color')
      .tipsy({title:"data-tipsy"});

    this.cleanTooltips();

    return this;
  },

  /**
  * Checks every subview and mark as selected if necesary
  * @method markSelectedLayer
  */
  markSelectedLayer: function() {
    for(var cid in this._subviews) {
      this._subviews[cid].checkIfSelected();
    }
  }

});
<|endoftext|>


<commit_before>
var after = require('after');
var bind = require('bind');
var callback = require('callback');
var canonical = require('canonical');
var clone = require('clone');
var cookie = require('./cookie');
var debug = require('debug');
var defaults = require('defaults');
var each = require('each');
var Emitter = require('emitter');
var group = require('./group');
var is = require('is');
var isEmail = require('is-email');
var isMeta = require('is-meta');
var newDate = require('new-date');
var on = require('event').bind;
var prevent = require('prevent');
var querystring = require('querystring');
var size = require('object').length;
var store = require('./store');
var traverse = require('isodate-traverse');
var url = require('url');
var user = require('./user');

/**
 * Expose `Analytics`.
 */

module.exports = Analytics;

/**
 * Initialize a new `Analytics` instance.
 */

function Analytics () {
  this.Integrations = {};
  this._integrations = {};
  this._readied = false;
  this._timeout = 300;
  this._user = user; // BACKWARDS COMPATIBILITY
  bind.all(this);
}

/**
 * Event Emitter.
 */

Emitter(Analytics.prototype);

/**
 * Use a `plugin`.
 *
 * @param {Function} plugin
 * @return {Analytics}
 */

Analytics.prototype.use = function (plugin) {
  plugin(this);
  return this;
};

/**
 * Define a new `Integration`.
 *
 * @param {Function} Integration
 * @return {Analytics}
 */

Analytics.prototype.addIntegration = function (Integration) {
  var name = Integration.prototype.name;
  this.Integrations[name] = Integration;
  return this;
};

/**
 * Initialize with the given integration `settings` and `options`. Aliased to
 * `init` for convenience.
 *
 * @param {Object} settings
 * @param {Object} options (optional)
 * @return {Analytics}
 */

Analytics.prototype.init =
Analytics.prototype.initialize = function (settings, options) {
  settings = settings || {};
  options = options || {};

  this._options(options);
  this._readied = false;
  this._integrations = {};

  // load user now that options are set
  user.load();
  group.load();

  // clean unknown integrations from settings
  var self = this;
  each(settings, function (name) {
    var Integration = self.Integrations[name];
    if (!Integration) delete settings[name];
  });

  // make ready callback
  var ready = after(size(settings), function () {
    self._readied = true;
    self.emit('ready');
  });

  // initialize integrations, passing ready
  each(settings, function (name, options) {
    var Integration = self.Integrations[name];
    var integration = new Integration(clone(options));
    integration.once('ready', ready);
    integration.initialize();
    self._integrations[name] = integration;
  });

  // call any querystring methods if present
  this._parseQuery();

  // backwards compat with angular plugin.
  // TODO: remove
  this.initialized = true;

  this.emit('initialize');

  return this;
};

/**
 * Identify a user by optional `id` and `traits`.
 *
 * @param {String} id (optional)
 * @param {Object} traits (optional)
 * @param {Object} options (optional)
 * @param {Function} fn (optional)
 * @return {Analytics}
 */

Analytics.prototype.identify = function (id, traits, options, fn) {
  if (is.fn(options)) fn = options, options = undefined;
  if (is.fn(traits)) fn = traits, traits = undefined;
  if (is.object(id)) options = traits, traits = id, id = user.id();

  user.identify(id, traits);

  // clone traits before we manipulate so we don't do anything uncouth, and take
  // from `user` so that we carryover anonymous traits
  id = user.id();
  traits = cleanTraits(id, user.traits());

  this._invoke('identify', id, traits, options);
  this._callback(fn);
  return this;
};

/**
 * Return the current user.
 *
 * @return {Object}
 */

Analytics.prototype.user = function () {
  return user;
};

/**
 * Identify a group by optional `id` and `properties`. Or, if no arguments are
 * supplied, return the current group.
 *
 * @param {String} id (optional)
 * @param {Object} properties (optional)
 * @param {Object} options (optional)
 * @param {Function} fn (optional)
 * @return {Analytics or Object}
 */

Analytics.prototype.group = function (id, properties, options, fn) {
  if (0 === arguments.length) return group;
  if (is.fn(options)) fn = options, options = undefined;
  if (is.fn(properties)) fn = properties, properties = undefined;
  if (is.object(id)) options = properties, properties = id, id = group.id();

  group.identify(id, properties);

  // grab from group again to make sure we're taking from the source
  id = group.id();
  properties = group.properties();

  // convert a create date to a date
  if (properties.created) properties.created = newDate(properties.created);

  this._invoke('group', id, properties, options);
  this._callback(fn);
  return this;
};

/**
 * Track an `event` that a user has triggered with optional `properties`.
 *
 * @param {String} event
 * @param {Object} properties (optional)
 * @param {Object} options (optional)
 * @param {Function} fn (optional)
 * @return {Analytics}
 */

Analytics.prototype.track = function (event, properties, options, fn) {
  if (is.fn(options)) fn = options, options = undefined;
  if (is.fn(properties)) fn = properties, properties = undefined;

  properties = traverse(clone(properties)) || {};

  this._invoke('track', event, properties, options);
  this._callback(fn);
  return this;
};

/**
 * Helper method to track an outbound link that would normally navigate away
 * from the page before the analytics calls were sent.
 *
 * BACKWARDS COMPATIBILITY: aliased to `trackClick`.
 *
 * @param {Element or Array} links
 * @param {String or Function} event
 * @param {Object or Function} properties (optional)
 * @return {Analytics}
 */

Analytics.prototype.trackClick =
Analytics.prototype.trackLink = function (links, event, properties) {
  if (!links) return this;
  if (is.element(links)) links = [links]; // always arrays, handles jquery

  var self = this;
  each(links, function (el) {
    on(el, 'click', function (e) {
      var ev = is.fn(event) ? event(el) : event;
      var props = is.fn(properties) ? properties(el) : properties;
      self.track(ev, props);

      if (el.href && el.target !== '_blank' && !isMeta(e)) {
        prevent(e);
        self._callback(function () {
          window.location.href = el.href;
        });
      }
    });
  });

  return this;
};

/**
 * Helper method to track an outbound form that would normally navigate away
 * from the page before the analytics calls were sent.
 *
 * BACKWARDS COMPATIBILITY: aliased to `trackSubmit`.
 *
 * @param {Element or Array} forms
 * @param {String or Function} event
 * @param {Object or Function} properties (optional)
 * @return {Analytics}
 */

Analytics.prototype.trackSubmit =
Analytics.prototype.trackForm = function (forms, event, properties) {
  if (!forms) return this;
  if (is.element(forms)) forms = [forms]; // always arrays, handles jquery

  var self = this;
  each(forms, function (el) {
    function handler (e) {
      prevent(e);

      var ev = is.fn(event) ? event(el) : event;
      var props = is.fn(properties) ? properties(el) : properties;
      self.track(ev, props);

      self._callback(function () {
        el.submit();
      });
    }

    // support the events happening through jQuery or Zepto instead of through
    // the normal DOM API, since `el.submit` doesn't bubble up events...
    var $ = window.jQuery || window.Zepto;
    if ($) {
      $(el).submit(handler);
    } else {
      on(el, 'submit', handler);
    }
  });

  return this;
};

/**
 * Trigger a pageview, labeling the current page with an optional `name` and
 * `properties`.
 *
 * @param {String} name (optional)
 * @param {Object or String} properties (or path) (optional)
 * @param {Object} options (optional)
 * @param {Function} fn (optional)
 * @return {Analytics}
 */

Analytics.prototype.page = function (name, properties, options, fn) {
  if (is.fn(options)) fn = options, options = undefined;
  if (is.fn(properties)) fn = properties, properties = undefined;
  if (is.object(name)) options = properties, properties = name, name = undefined;
  if (is.string(properties)) properties = { path: properties };

  properties = clone(properties) || {};
  defaults(properties, {
    path: canonicalPath(),
    referrer: document.referrer,
    title: document.title,
    url: location.href
  });

  this._invoke('page', name, properties, options);
  this._callback(fn);
  return this;
};

/**
 * BACKWARDS COMPATIBILITY: convert an old `pageview` to a `page` call.
 *
 * @param {String} url (optional)
 * @param {Object} options (optional)
 * @return {Analytics}
 * @api private
 */

Analytics.prototype.pageview = function (url, options) {
  var properties = {};
  if (url) properties.path = url;
  this.page(properties);
  return this;
};

/**
 * Merge two previously unassociated user identities.
 *
 * @param {String} to
 * @param {String} from (optional)
 * @param {Object} options (optional)
 * @param {Function} fn (optional)
 * @return {Analytics}
 */

Analytics.prototype.alias = function (to, from, options, fn) {
  if (is.fn(options)) fn = options, options = undefined;
  if (is.fn(from)) fn = from, from = undefined;
  if (is.object(from)) options = from, from = undefined;
  this._invoke('alias', to, from, options);
  this._callback(fn);
  return this;
};

/**
 * Register a `fn` to be fired when all the analytics services are ready.
 *
 * @param {Function} fn
 * @return {Analytics}
 */

Analytics.prototype.ready = function (fn) {
  if (!is.fn(fn)) return this;
  this._readied
    ? callback.async(fn)
    : this.once('ready', fn);
  return this;
};

/**
 * Set the `timeout` (in milliseconds) used for callbacks.
 *
 * @param {Number} timeout
 */

Analytics.prototype.timeout = function (timeout) {
  this._timeout = timeout;
};

/**
 * Enable or disable debug.
 *
 * @param {String or Boolean} str
 */

Analytics.prototype.debug = function(str){
  if (0 == arguments.length || str) {
    debug.enable('analytics:' + (str || '*'));
  } else {
    debug.disable();
  }
};

/**
 * Apply options.
 *
 * @param {Object} options
 * @return {Analytics}
 * @api private
 */

Analytics.prototype._options = function (options) {
  options = options || {};
  cookie.options(options.cookie);
  store.options(options.localStorage);
  user.options(options.user);
  group.options(options.group);
  return this;
};

/**
 * Callback a `fn` after our defined timeout period.
 *
 * @param {Function} fn
 * @return {Analytics}
 * @api private
 */

Analytics.prototype._callback = function (fn) {
  callback.async(fn, this._timeout);
  return this;
};

/**
 * Call a `method` on all of initialized integrations, passing clones of arguments
 * along to keep each integration isolated.
 *
 * TODO: check integration enabled
 *
 * @param {String} method
 * @param {Mixed} args...
 * @return {Analytics}
 * @api private
 */

Analytics.prototype._invoke = function () {
  var args = [].slice.call(arguments);
  var options = args[args.length-1]; // always the last one

  each(this._integrations, function (name, integration) {
    if (!isEnabled(integration, options)) return;
    integration.invoke.apply(integration, clone(args));
  });

  this.emit.apply(this, clone(args));
  return this;
};

/**
 * Parse the query string for callable methods.
 *
 * @return {Analytics}
 * @api private
 */

Analytics.prototype._parseQuery = function () {
  // Identify and track any `ajs_uid` and `ajs_event` parameters in the URL.
  var q = querystring.parse(window.location.search);
  if (q.ajs_uid) this.identify(q.ajs_uid);
  if (q.ajs_event) this.track(q.ajs_event);
  return this;
};

/**
 * Determine whether a `integration` is enabled or not based on `options`.
 *
 * @param {Object} integration
 * @param {Object} options
 * @return {Boolean}
 */

function isEnabled (integration, options) {
  var enabled = true;
  if (!options || !options.providers) return enabled;

  // Default to the 'all' or 'All' setting.
  var map = options.providers;
  if (map.all !== undefined) enabled = map.all;
  if (map.All !== undefined) enabled = map.All;

  // Look for this integration's specific setting.
  var name = integration.name;
  if (map[name] !== undefined) enabled = map[name];

  return enabled;
}

/**
 * Clean up traits, default some useful things both so the user doesn't have to
 * and so we don't have to do it on a integration-basis.
 *
 * @param {Object} traits
 * @return {Object}
 */

function cleanTraits (userId, traits) {
  // Add the `email` trait if it doesn't exist and the `userId` is an email.
  if (!traits.email && isEmail(userId)) traits.email = userId;

  // Create the `name` trait if it doesn't exist and `firstName` and `lastName`
  // are both supplied.
  if (!traits.name && traits.firstName && traits.lastName) {
    traits.name = traits.firstName + ' ' + traits.lastName;
  }

  // Convert dates from more types of input into Date objects.
  if (traits.created) traits.created = newDate(traits.created);
  if (traits.company && traits.company.created) {
    traits.company.created = newDate(traits.company.created);
  }

  return traits;
}

/**
 * Return the canonical path for the page.
 *
 * @return {String}
 */

function canonicalPath () {
  var canon = canonical();
  if (!canon) return window.location.pathname;
  var parsed = url.parse(canon);
  return parsed.pathname;
}
<commit_msg>adding backwards compat to .page<commit_after>
var after = require('after');
var bind = require('bind');
var callback = require('callback');
var canonical = require('canonical');
var clone = require('clone');
var cookie = require('./cookie');
var debug = require('debug');
var defaults = require('defaults');
var each = require('each');
var Emitter = require('emitter');
var group = require('./group');
var is = require('is');
var isEmail = require('is-email');
var isMeta = require('is-meta');
var newDate = require('new-date');
var on = require('event').bind;
var prevent = require('prevent');
var querystring = require('querystring');
var size = require('object').length;
var store = require('./store');
var traverse = require('isodate-traverse');
var url = require('url');
var user = require('./user');

/**
 * Expose `Analytics`.
 */

module.exports = Analytics;

/**
 * Initialize a new `Analytics` instance.
 */

function Analytics () {
  this.Integrations = {};
  this._integrations = {};
  this._readied = false;
  this._timeout = 300;
  this._user = user; // BACKWARDS COMPATIBILITY
  bind.all(this);

  this.on('initialize', function (settings, options) {
    if (options.initialPageview) this.page();
  });

  this.on('initialize', this._parseQuery);

}

/**
 * Event Emitter.
 */

Emitter(Analytics.prototype);

/**
 * Use a `plugin`.
 *
 * @param {Function} plugin
 * @return {Analytics}
 */

Analytics.prototype.use = function (plugin) {
  plugin(this);
  return this;
};

/**
 * Define a new `Integration`.
 *
 * @param {Function} Integration
 * @return {Analytics}
 */

Analytics.prototype.addIntegration = function (Integration) {
  var name = Integration.prototype.name;
  this.Integrations[name] = Integration;
  return this;
};

/**
 * Initialize with the given integration `settings` and `options`. Aliased to
 * `init` for convenience.
 *
 * @param {Object} settings
 * @param {Object} options (optional)
 * @return {Analytics}
 */

Analytics.prototype.init =
Analytics.prototype.initialize = function (settings, options) {
  settings = settings || {};
  options = options || {};

  this._options(options);
  this._readied = false;
  this._integrations = {};

  // load user now that options are set
  user.load();
  group.load();

  // clean unknown integrations from settings
  var self = this;
  each(settings, function (name) {
    var Integration = self.Integrations[name];
    if (!Integration) delete settings[name];
  });

  // make ready callback
  var ready = after(size(settings), function () {
    self._readied = true;
    self.emit('ready');
  });

  // initialize integrations, passing ready
  each(settings, function (name, options) {
    var Integration = self.Integrations[name];
    if (options.initialPageview === false) {
      Integration.prototype.page = after(2, Integration.prototype.page);
    }

    var integration = new Integration(clone(options));
    integration.once('ready', ready);
    integration.initialize();
    self._integrations[name] = integration;
  });

  // backwards compat with angular plugin.
  // TODO: remove
  this.initialized = true;

  this.emit('initialize', settings, options);
  return this;
};

/**
 * Identify a user by optional `id` and `traits`.
 *
 * @param {String} id (optional)
 * @param {Object} traits (optional)
 * @param {Object} options (optional)
 * @param {Function} fn (optional)
 * @return {Analytics}
 */

Analytics.prototype.identify = function (id, traits, options, fn) {
  if (is.fn(options)) fn = options, options = undefined;
  if (is.fn(traits)) fn = traits, traits = undefined;
  if (is.object(id)) options = traits, traits = id, id = user.id();

  user.identify(id, traits);

  // clone traits before we manipulate so we don't do anything uncouth, and take
  // from `user` so that we carryover anonymous traits
  id = user.id();
  traits = cleanTraits(id, user.traits());

  this._invoke('identify', id, traits, options);
  this._callback(fn);
  return this;
};

/**
 * Return the current user.
 *
 * @return {Object}
 */

Analytics.prototype.user = function () {
  return user;
};

/**
 * Identify a group by optional `id` and `properties`. Or, if no arguments are
 * supplied, return the current group.
 *
 * @param {String} id (optional)
 * @param {Object} properties (optional)
 * @param {Object} options (optional)
 * @param {Function} fn (optional)
 * @return {Analytics or Object}
 */

Analytics.prototype.group = function (id, properties, options, fn) {
  if (0 === arguments.length) return group;
  if (is.fn(options)) fn = options, options = undefined;
  if (is.fn(properties)) fn = properties, properties = undefined;
  if (is.object(id)) options = properties, properties = id, id = group.id();

  group.identify(id, properties);

  // grab from group again to make sure we're taking from the source
  id = group.id();
  properties = group.properties();

  // convert a create date to a date
  if (properties.created) properties.created = newDate(properties.created);

  this._invoke('group', id, properties, options);
  this._callback(fn);
  return this;
};

/**
 * Track an `event` that a user has triggered with optional `properties`.
 *
 * @param {String} event
 * @param {Object} properties (optional)
 * @param {Object} options (optional)
 * @param {Function} fn (optional)
 * @return {Analytics}
 */

Analytics.prototype.track = function (event, properties, options, fn) {
  if (is.fn(options)) fn = options, options = undefined;
  if (is.fn(properties)) fn = properties, properties = undefined;

  properties = traverse(clone(properties)) || {};

  this._invoke('track', event, properties, options);
  this._callback(fn);
  return this;
};

/**
 * Helper method to track an outbound link that would normally navigate away
 * from the page before the analytics calls were sent.
 *
 * BACKWARDS COMPATIBILITY: aliased to `trackClick`.
 *
 * @param {Element or Array} links
 * @param {String or Function} event
 * @param {Object or Function} properties (optional)
 * @return {Analytics}
 */

Analytics.prototype.trackClick =
Analytics.prototype.trackLink = function (links, event, properties) {
  if (!links) return this;
  if (is.element(links)) links = [links]; // always arrays, handles jquery

  var self = this;
  each(links, function (el) {
    on(el, 'click', function (e) {
      var ev = is.fn(event) ? event(el) : event;
      var props = is.fn(properties) ? properties(el) : properties;
      self.track(ev, props);

      if (el.href && el.target !== '_blank' && !isMeta(e)) {
        prevent(e);
        self._callback(function () {
          window.location.href = el.href;
        });
      }
    });
  });

  return this;
};

/**
 * Helper method to track an outbound form that would normally navigate away
 * from the page before the analytics calls were sent.
 *
 * BACKWARDS COMPATIBILITY: aliased to `trackSubmit`.
 *
 * @param {Element or Array} forms
 * @param {String or Function} event
 * @param {Object or Function} properties (optional)
 * @return {Analytics}
 */

Analytics.prototype.trackSubmit =
Analytics.prototype.trackForm = function (forms, event, properties) {
  if (!forms) return this;
  if (is.element(forms)) forms = [forms]; // always arrays, handles jquery

  var self = this;
  each(forms, function (el) {
    function handler (e) {
      prevent(e);

      var ev = is.fn(event) ? event(el) : event;
      var props = is.fn(properties) ? properties(el) : properties;
      self.track(ev, props);

      self._callback(function () {
        el.submit();
      });
    }

    // support the events happening through jQuery or Zepto instead of through
    // the normal DOM API, since `el.submit` doesn't bubble up events...
    var $ = window.jQuery || window.Zepto;
    if ($) {
      $(el).submit(handler);
    } else {
      on(el, 'submit', handler);
    }
  });

  return this;
};

/**
 * Trigger a pageview, labeling the current page with an optional `name` and
 * `properties`.
 *
 * @param {String} name (optional)
 * @param {Object or String} properties (or path) (optional)
 * @param {Object} options (optional)
 * @param {Function} fn (optional)
 * @return {Analytics}
 */

Analytics.prototype.page = function (name, properties, options, fn) {
  if (is.fn(options)) fn = options, options = undefined;
  if (is.fn(properties)) fn = properties, properties = undefined;
  if (is.object(name)) options = properties, properties = name, name = undefined;
  if (is.string(properties)) properties = { path: properties };

  properties = clone(properties) || {};
  defaults(properties, {
    path: canonicalPath(),
    referrer: document.referrer,
    title: document.title,
    url: location.href
  });

  this._invoke('page', name, properties, options);
  this._callback(fn);
  return this;
};

/**
 * BACKWARDS COMPATIBILITY: convert an old `pageview` to a `page` call.
 *
 * @param {String} url (optional)
 * @param {Object} options (optional)
 * @return {Analytics}
 * @api private
 */

Analytics.prototype.pageview = function (url, options) {
  var properties = {};
  if (url) properties.path = url;
  this.page(properties);
  return this;
};

/**
 * Merge two previously unassociated user identities.
 *
 * @param {String} to
 * @param {String} from (optional)
 * @param {Object} options (optional)
 * @param {Function} fn (optional)
 * @return {Analytics}
 */

Analytics.prototype.alias = function (to, from, options, fn) {
  if (is.fn(options)) fn = options, options = undefined;
  if (is.fn(from)) fn = from, from = undefined;
  if (is.object(from)) options = from, from = undefined;
  this._invoke('alias', to, from, options);
  this._callback(fn);
  return this;
};

/**
 * Register a `fn` to be fired when all the analytics services are ready.
 *
 * @param {Function} fn
 * @return {Analytics}
 */

Analytics.prototype.ready = function (fn) {
  if (!is.fn(fn)) return this;
  this._readied
    ? callback.async(fn)
    : this.once('ready', fn);
  return this;
};

/**
 * Set the `timeout` (in milliseconds) used for callbacks.
 *
 * @param {Number} timeout
 */

Analytics.prototype.timeout = function (timeout) {
  this._timeout = timeout;
};

/**
 * Enable or disable debug.
 *
 * @param {String or Boolean} str
 */

Analytics.prototype.debug = function(str){
  if (0 == arguments.length || str) {
    debug.enable('analytics:' + (str || '*'));
  } else {
    debug.disable();
  }
};

/**
 * Apply options.
 *
 * @param {Object} options
 * @return {Analytics}
 * @api private
 */

Analytics.prototype._options = function (options) {
  options = options || {};
  cookie.options(options.cookie);
  store.options(options.localStorage);
  user.options(options.user);
  group.options(options.group);
  return this;
};

/**
 * Callback a `fn` after our defined timeout period.
 *
 * @param {Function} fn
 * @return {Analytics}
 * @api private
 */

Analytics.prototype._callback = function (fn) {
  callback.async(fn, this._timeout);
  return this;
};

/**
 * Call a `method` on all of initialized integrations, passing clones of arguments
 * along to keep each integration isolated.
 *
 * TODO: check integration enabled
 *
 * @param {String} method
 * @param {Mixed} args...
 * @return {Analytics}
 * @api private
 */

Analytics.prototype._invoke = function () {
  var args = [].slice.call(arguments);
  var options = args[args.length-1]; // always the last one

  each(this._integrations, function (name, integration) {
    if (!isEnabled(integration, options)) return;
    integration.invoke.apply(integration, clone(args));
  });

  this.emit.apply(this, clone(args));
  return this;
};

/**
 * Parse the query string for callable methods.
 *
 * @return {Analytics}
 * @api private
 */

Analytics.prototype._parseQuery = function () {
  // Identify and track any `ajs_uid` and `ajs_event` parameters in the URL.
  var q = querystring.parse(window.location.search);
  if (q.ajs_uid) this.identify(q.ajs_uid);
  if (q.ajs_event) this.track(q.ajs_event);
  return this;
};

/**
 * Determine whether a `integration` is enabled or not based on `options`.
 *
 * @param {Object} integration
 * @param {Object} options
 * @return {Boolean}
 */

function isEnabled (integration, options) {
  var enabled = true;
  if (!options || !options.providers) return enabled;

  // Default to the 'all' or 'All' setting.
  var map = options.providers;
  if (map.all !== undefined) enabled = map.all;
  if (map.All !== undefined) enabled = map.All;

  // Look for this integration's specific setting.
  var name = integration.name;
  if (map[name] !== undefined) enabled = map[name];

  return enabled;
}

/**
 * Clean up traits, default some useful things both so the user doesn't have to
 * and so we don't have to do it on a integration-basis.
 *
 * @param {Object} traits
 * @return {Object}
 */

function cleanTraits (userId, traits) {
  // Add the `email` trait if it doesn't exist and the `userId` is an email.
  if (!traits.email && isEmail(userId)) traits.email = userId;

  // Create the `name` trait if it doesn't exist and `firstName` and `lastName`
  // are both supplied.
  if (!traits.name && traits.firstName && traits.lastName) {
    traits.name = traits.firstName + ' ' + traits.lastName;
  }

  // Convert dates from more types of input into Date objects.
  if (traits.created) traits.created = newDate(traits.created);
  if (traits.company && traits.company.created) {
    traits.company.created = newDate(traits.company.created);
  }

  return traits;
}

/**
 * Return the canonical path for the page.
 *
 * @return {String}
 */

function canonicalPath () {
  var canon = canonical();
  if (!canon) return window.location.pathname;
  var parsed = url.parse(canon);
  return parsed.pathname;
}
<|endoftext|>


<commit_before>
/*!
 * Common test dependencies.
 */

// expose the globals that are obtained through `<script>` on the browser
expect = require('expect.js')
eio = require('../lib/engine.io-client')
<commit_msg>test: fix dependencies<commit_after>
/*!
 * Common test dependencies.
 */

// expose the globals that are obtained through `<script>` on the browser
expect = require('expect.js');
eio = require('../lib/');
<|endoftext|>