This commit is contained in:
William Durand 2020-08-03 14:55:07 +02:00 коммит произвёл GitHub
Родитель f93cc66f3a
Коммит c2e92e7482
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
93 изменённых файлов: 31789 добавлений и 29185 удалений

29
.prettierignore Normal file
Просмотреть файл

@ -0,0 +1,29 @@
# exclude everything by default
*.*
# exclude these files
Dockerfile*
LICENSE
Makefile*
# exclude these directories
.pytest_cache/
.tox/
/docker/
/docs/
/private/
/scripts/
/site-static/
/src/
/storage/
__pycache__/
i18n/
img/
node_lib/
# safe-list files we want to process
!*.js
# exclude generated JS files
*-all.js
*-min.js

6
.prettierrc Normal file
Просмотреть файл

@ -0,0 +1,6 @@
{
"arrowParens": "always",
"singleQuote": true,
"trailingComma": "all",
"proseWrap": "never"
}

Просмотреть файл

@ -190,6 +190,7 @@ setup-codestyle:
.PHONY: lint .PHONY: lint
lint: ## lint the code lint: ## lint the code
flake8 src/ services/ tests/ flake8 src/ services/ tests/
$(shell npm $(NPM_ARGS) bin)/prettier --check '**'
lint-codestyle: lint lint-codestyle: lint
@ -250,6 +251,10 @@ run_js_tests: ## Run the JavaScript test suite (requires compiled/compressed ass
watch_js_tests: ## Run+watch the JavaScript test suite (requires compiled/compressed assets). watch_js_tests: ## Run+watch the JavaScript test suite (requires compiled/compressed assets).
NODE_PATH=$(NODE_MODULES) $$(npm bin $(NPM_ARGS))/jest --watch NODE_PATH=$(NODE_MODULES) $$(npm bin $(NPM_ARGS))/jest --watch
.PHONY: format
format: ## Autoformat our codebase.
$(shell npm $(NPM_ARGS) bin)/prettier --write '**'
.PHONY: help_submake .PHONY: help_submake
help_submake: help_submake:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile-docker | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile-docker | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

Просмотреть файл

@ -6,16 +6,16 @@
"node": ">= 10.13" "node": ">= 10.13"
}, },
"dependencies": { "dependencies": {
"@claviska/jquery-minicolors": "2.3.5",
"addons-linter": "2.1.0", "addons-linter": "2.1.0",
"clean-css": "4.2.3", "clean-css": "4.2.3",
"clean-css-cli": "4.3.0", "clean-css-cli": "4.3.0",
"jqmodal": "1.4.2", "jqmodal": "1.4.2",
"jquery": "3.5.1", "jquery": "3.5.1",
"jquery.browser": "0.1.0",
"jquery.cookie": "1.4.1",
"@claviska/jquery-minicolors": "2.3.5",
"jquery-pjax": "2.0.1", "jquery-pjax": "2.0.1",
"jquery-ui": "1.12.1", "jquery-ui": "1.12.1",
"jquery.browser": "0.1.0",
"jquery.cookie": "1.4.1",
"jszip": "3.5.0", "jszip": "3.5.0",
"less": "3.12.2", "less": "3.12.2",
"source-map": "0.7.3", "source-map": "0.7.3",
@ -25,6 +25,7 @@
}, },
"devDependencies": { "devDependencies": {
"jest": "^26.1.0", "jest": "^26.1.0",
"jest-date-mock": "^1.0.8" "jest-date-mock": "^1.0.8",
"prettier": "^2.0.5"
} }
} }

Просмотреть файл

@ -8,19 +8,19 @@
*/ */
django.jQuery(document).ready(function ($) { django.jQuery(document).ready(function ($) {
if (!$('body.change-form').length) { if (!$('body.change-form').length) {
// This is only for change forms. // This is only for change forms.
return; return;
} }
// Each localized field will be inside a <div class="trans">. We are // Each localized field will be inside a <div class="trans">. We are
// displaying all of them, so we want to add individual labels to let the // displaying all of them, so we want to add individual labels to let the
// user know which one is for which locale. // user know which one is for which locale.
$('div.trans :input:visible').before(function() { $('div.trans :input:visible').before(function () {
let $elm = $(this); let $elm = $(this);
let $label = $('<label>'); let $label = $('<label>');
$label.prop('for', $elm.attr('id')) $label.prop('for', $elm.attr('id'));
$label.text('[' + $elm.attr('lang') + ']'); $label.text('[' + $elm.attr('lang') + ']');
return $label; return $label;
}) });
}); });

Просмотреть файл

@ -1,31 +1,30 @@
django.jQuery(document).ready(function ($){ django.jQuery(document).ready(function ($) {
"use strict"; 'use strict';
// Recalculate Hash // Recalculate Hash
$('.recalc').click(function(e) { $('.recalc').click(function (e) {
var $this = $(this), var $this = $(this),
csrf = $("input[name='csrfmiddlewaretoken']").val(); csrf = $("input[name='csrfmiddlewaretoken']").val();
e.preventDefault(); e.preventDefault();
$.ajax($this.attr('href'), { $.ajax($this.attr('href'), {
headers: { 'X-CSRFToken': csrf },
"headers": {'X-CSRFToken': csrf}, dataType: 'json',
"dataType": "json", method: 'POST',
"method": "POST", beforeSend: function () {
"beforeSend": function () { $this.html('Recalcing&hellip;');
$this.html('Recalcing&hellip;'); },
}, success: function () {
"success": function() { $this.text('Done!');
$this.text('Done!'); },
}, error: function () {
"error": function() { $this.text('Error :(');
$this.text('Error :('); },
}, complete: function () {
"complete": function() { setTimeout(function () {
setTimeout(function() { $this.text('Recalc Hash');
$this.text('Recalc Hash'); }, 2000);
}, 2000); },
}
});
}); });
});
}); });

Просмотреть файл

@ -1,24 +1,30 @@
/* TODO(jbalogh): save from amo2009. */ /* TODO(jbalogh): save from amo2009. */
/** /**
* bandwagon: fire a custom refresh event for bandwagon extension * bandwagon: fire a custom refresh event for bandwagon extension
* @return void * @return void
*/ */
function bandwagonRefreshEvent() { function bandwagonRefreshEvent() {
if (document.createEvent) { if (document.createEvent) {
var bandwagonSubscriptionsRefreshEvent = document.createEvent("Events"); var bandwagonSubscriptionsRefreshEvent = document.createEvent('Events');
bandwagonSubscriptionsRefreshEvent.initEvent("bandwagonRefresh", true, false); bandwagonSubscriptionsRefreshEvent.initEvent(
document.dispatchEvent(bandwagonSubscriptionsRefreshEvent); 'bandwagonRefresh',
} true,
false,
);
document.dispatchEvent(bandwagonSubscriptionsRefreshEvent);
}
} }
/* TODO(jbalogh): save from amo2009. */ /* TODO(jbalogh): save from amo2009. */
/* Remove "Go" buttons from <form class="go" */ /* Remove "Go" buttons from <form class="go" */
$(document).ready(function(){ $(document).ready(function () {
$('form.go').change(function() { this.submit(); }) $('form.go')
.find('button').hide(); .change(function () {
this.submit();
})
.find('button')
.hide();
}); });
// TODO(jbalogh): save from amo2009. // TODO(jbalogh): save from amo2009.
var AMO = {}; var AMO = {};

Просмотреть файл

@ -1,288 +1,311 @@
(function($) { (function ($) {
window.Slideshow = function() { window.Slideshow = function () {
this.itemTotal = 0; this.itemTotal = 0;
this.currentItem = 1; this.currentItem = 1;
this.itemWidth = 0; this.itemWidth = 0;
// Set these properties when you instantiate an instance of this object. // Set these properties when you instantiate an instance of this object.
this.speed = 300; // the speed in milliseconds of the animation this.speed = 300; // the speed in milliseconds of the animation
this.itemContainer = ''; // the selector for the element containing the items. this.itemContainer = ''; // the selector for the element containing the items.
this.wrapperElement = ''; // the tagName that will wrap the itemContainer. this.wrapperElement = ''; // the tagName that will wrap the itemContainer.
this.wrapperClass = ''; //the classname of the element that will wrap the itemContainer. this.wrapperClass = ''; //the classname of the element that will wrap the itemContainer.
this.controlsMarkup = ''; // the markup for the controls. this.controlsMarkup = ''; // the markup for the controls.
this.leftController = ''; // the selector for the left controller. this.leftController = ''; // the selector for the left controller.
this.rightContorller = ''; // the selector for the right controller. this.rightContorller = ''; // the selector for the right controller.
this.activeClass = ''; // the classname to indicate that a controller is active. this.activeClass = ''; // the classname to indicate that a controller is active.
this.container = ''; //the complete container for all of the slideshow this.container = ''; //the complete container for all of the slideshow
this.interval = null; this.interval = null;
this.scroll = true; this.scroll = true;
}; };
Slideshow.prototype.init = function() { Slideshow.prototype.init = function () {
this.itemTotal = parseInt($(this.itemContainer+'>li').length,10); this.itemTotal = parseInt($(this.itemContainer + '>li').length, 10);
if (this.itemTotal <= 1) { if (this.itemTotal <= 1) {
return; return;
} }
$(this.itemContainer).wrap('<'+this.wrapperElement+' class="'+this.wrapperClass+'"></'+this.wrapperElement+'>'); $(this.itemContainer).wrap(
this.itemWidth = this.getItemWidth(); '<' +
// applying controls to 2nd parent rather than 1st fixes stacking context issue in FF2 this.wrapperElement +
$($(this.itemContainer).parents()[1]).append(this.controlsMarkup); ' class="' +
$(this.itemContainer+'>li').width(this.itemWidth+'px'); this.wrapperClass +
'"></' +
this.wrapperElement +
'>',
);
this.itemWidth = this.getItemWidth();
// applying controls to 2nd parent rather than 1st fixes stacking context issue in FF2
$($(this.itemContainer).parents()[1]).append(this.controlsMarkup);
$(this.itemContainer + '>li').width(this.itemWidth + 'px');
this.checkControls(); this.checkControls();
var self = this; var self = this;
$(self.leftController).live('click', function() { $(self.leftController).live('click', function () {
if ($(this).hasClass(self.activeClass)) { if ($(this).hasClass(self.activeClass)) {
self.moveToItem(self.currentItem-1); self.moveToItem(self.currentItem - 1);
} }
self.scroll = false; self.scroll = false;
return false; return false;
}); });
$(self.rightController).live('click', function() { $(self.rightController).live('click', function () {
if ($(this).hasClass(self.activeClass)) { if ($(this).hasClass(self.activeClass)) {
self.moveToItem(self.currentItem+1); self.moveToItem(self.currentItem + 1);
} }
self.scroll = false; self.scroll = false;
return false; return false;
}); });
$(self.container).mouseenter(function() { $(self.container).mouseenter(function () {
clearInterval(self.interval); clearInterval(self.interval);
}); });
$(self.container).on('newPopup', function() { $(self.container).on('newPopup', function () {
clearInterval(self.interval); clearInterval(self.interval);
}); });
$(self.container).mouseleave(function() { $(self.container).mouseleave(function () {
self.autoRotate(); self.autoRotate();
}); });
self.autoRotate(); self.autoRotate();
$(window).resize(function() { $(window).resize(function () {
self.itemWidth = self.getItemWidth(); self.itemWidth = self.getItemWidth();
$(self.itemContainer+'>li').width(self.itemWidth+'px'); $(self.itemContainer + '>li').width(self.itemWidth + 'px');
self.popToItem(self.currentItem); self.popToItem(self.currentItem);
}); });
}; };
Slideshow.prototype.autoRotate = function() { Slideshow.prototype.autoRotate = function () {
if(this.scroll) { if (this.scroll) {
var that = this; //closure due to setInterval's 'this' refers to window, not the current 'this' var that = this; //closure due to setInterval's 'this' refers to window, not the current 'this'
clearInterval(this.interval); clearInterval(this.interval);
this.interval = setInterval(function() { this.interval = setInterval(function () {
if(that.currentItem != that.itemTotal) { if (that.currentItem != that.itemTotal) {
that.moveToItem(that.currentItem+1); that.moveToItem(that.currentItem + 1);
} else { } else {
that.moveToItem(1); that.moveToItem(1);
} }
}, 8000); }, 8000);
} }
}; };
Slideshow.prototype.getItemWidth = function() { Slideshow.prototype.getItemWidth = function () {
return $(this.itemContainer).parents('.'+this.wrapperClass).width(); return $(this.itemContainer)
}; .parents('.' + this.wrapperClass)
Slideshow.prototype.popToItem = function(itemNumber) { .width();
if (!$(this.itemContainer).parents('.'+this.wrapperClass+' :animated').length) { };
$($(this.itemContainer).children("li").get(this.currentItem-1)).hide(); Slideshow.prototype.popToItem = function (itemNumber) {
$($(this.itemContainer).children("li").get(itemNumber-1)).show(); if (
this.currentItem = itemNumber; !$(this.itemContainer).parents('.' + this.wrapperClass + ' :animated')
this.checkControls(); .length
} ) {
}; $(
Slideshow.prototype.moveToItem = function(itemNumber) { $(this.itemContainer)
if (!$(this.itemContainer).parents('.'+this.wrapperClass+' :animated').length) { .children('li')
var lis = $(this.itemContainer).children("li"); .get(this.currentItem - 1),
$(lis.get(this.currentItem-1)).fadeOut("fast", function () { ).hide();
$(lis.get(itemNumber-1)).fadeIn("fast"); $(
}); $(this.itemContainer)
this.currentItem = itemNumber; .children('li')
this.checkControls(); .get(itemNumber - 1),
} ).show();
}; this.currentItem = itemNumber;
Slideshow.prototype.checkControls = function() { this.checkControls();
if (this.currentItem == 1) { }
$(this.leftController).removeClass(this.activeClass); };
} else { Slideshow.prototype.moveToItem = function (itemNumber) {
$(this.leftController).addClass(this.activeClass); if (
} !$(this.itemContainer).parents('.' + this.wrapperClass + ' :animated')
if (this.currentItem == this.itemTotal) { .length
$(this.rightController).removeClass(this.activeClass); ) {
} else { var lis = $(this.itemContainer).children('li');
$(this.rightController).addClass(this.activeClass); $(lis.get(this.currentItem - 1)).fadeOut('fast', function () {
} $(lis.get(itemNumber - 1)).fadeIn('fast');
}; });
this.currentItem = itemNumber;
this.checkControls();
}
};
Slideshow.prototype.checkControls = function () {
if (this.currentItem == 1) {
$(this.leftController).removeClass(this.activeClass);
} else {
$(this.leftController).addClass(this.activeClass);
}
if (this.currentItem == this.itemTotal) {
$(this.rightController).removeClass(this.activeClass);
} else {
$(this.rightController).addClass(this.activeClass);
}
};
// slidey dropdown area // slidey dropdown area
window.DropdownArea = function() { window.DropdownArea = function () {
this.trigger = null; this.trigger = null;
this.target = ''; this.target = '';
this.targetParent = ''; this.targetParent = '';
this.callbackFunction = function(){}; this.callbackFunction = function () {};
this.preventDefault = true; this.preventDefault = true;
this.showSpeed = 200; this.showSpeed = 200;
this.hideSpeed = 200; this.hideSpeed = 200;
this.hideOnBodyClick = true; this.hideOnBodyClick = true;
}; };
DropdownArea.prototype.bodyclick = function(e) { DropdownArea.prototype.bodyclick = function (e) {
// this will get fired on click of body, we need to close the dropdown // this will get fired on click of body, we need to close the dropdown
if (this.bodyWatching && this.hideOnBodyClick) { if (this.bodyWatching && this.hideOnBodyClick) {
if (! if (
($(e.target).get(0) == $(this.targetParent).get(0) || !(
$(e.target).parents(this.targetParent).length) $(e.target).get(0) == $(this.targetParent).get(0) ||
) { $(e.target).parents(this.targetParent).length
this.hide(); )
} ) {
this.hide();
} }
} }
DropdownArea.prototype.hide = function() { };
var self = this; DropdownArea.prototype.hide = function () {
$(self.targetParent).removeClass('expanded'); var self = this;
$(self.target).slideUp(self.hideSpeed, function() { $(self.targetParent).removeClass('expanded');
//unbind bodyclick $(self.target).slideUp(self.hideSpeed, function () {
self.bodyWatching = false; //unbind bodyclick
}); self.bodyWatching = false;
} });
DropdownArea.prototype.show = function() { };
var self = this; DropdownArea.prototype.show = function () {
$(self.targetParent).addClass('expanded'); var self = this;
$(self.target).slideDown(self.showSpeed, function() { $(self.targetParent).addClass('expanded');
self.bodyWatching = true; $(self.target).slideDown(self.showSpeed, function () {
}); self.bodyWatching = true;
} });
DropdownArea.prototype.init = function() { };
DropdownArea.prototype.init = function () {
// advanced dropdown // advanced dropdown
var self = this; var self = this;
$(this.target).hide(); $(this.target).hide();
if (this.trigger) { if (this.trigger) {
this.trigger.click( this.trigger.click(function (e) {
function(e) { if (!$(self.target + ':animated').length) {
if(! $(self.target+':animated').length) { if ($(self.target + ':visible').length) {
if ($(self.target+':visible').length){ self.callbackFunction();
self.callbackFunction(); self.hide();
self.hide(); } else {
} else { self.callbackFunction();
self.callbackFunction(); self.show();
self.show(); }
}
}
$(self.target).trigger('click');
return !self.preventDefault;
}
);
// if box now showing bind bodyclick
$('body').on("click", function(e) {
self.bodyclick(e);
});
}
};
// A special slideshow that updates the teaser 'selected' list item
window.AmoSlideshow = function() {
/* This is a convenience function that performs all the slideshow
* setup we shouldn't have to think about if the slideshow code
* was written with an eye for abstraction and reusability.
* First one to refactor it gets a cookie.
*/
function HeaderSlideshow() {
if($('.teaser-items').hasClass('no-autorotate')) {
Slideshow.prototype.autoRotate = function(){}
}
Slideshow.call(this);
} }
HeaderSlideshow.prototype = new Slideshow(); $(self.target).trigger('click');
HeaderSlideshow.prototype.moveToItem = function(itemNumber) { return !self.preventDefault;
Slideshow.prototype.moveToItem.call(this, itemNumber); });
$('.section-teaser .teaser-header li').removeClass('selected'); // if box now showing bind bodyclick
$('.section-teaser .teaser-header li').eq(itemNumber - 1).addClass('selected'); $('body').on('click', function (e) {
}; self.bodyclick(e);
});
}
};
var homepageSlider = new HeaderSlideshow(); // A special slideshow that updates the teaser 'selected' list item
homepageSlider.itemContainer = '.teaser-items'; window.AmoSlideshow = function () {
homepageSlider.wrapperElement = 'div'; /* This is a convenience function that performs all the slideshow
homepageSlider.wrapperClass = 'window'; * setup we shouldn't have to think about if the slideshow code
homepageSlider.controlsMarkup = ( * was written with an eye for abstraction and reusability.
'<p class="slideshow-controls">' + * First one to refactor it gets a cookie.
'<a href="#" class="prev" rel="prev">Previous</a>' + */
'<a href="#" class="next" rel="next">Next</a></p>' function HeaderSlideshow() {
); if ($('.teaser-items').hasClass('no-autorotate')) {
homepageSlider.leftController = '.section-teaser a[rel="prev"]'; Slideshow.prototype.autoRotate = function () {};
homepageSlider.rightController = '.section-teaser a[rel="next"]'; }
homepageSlider.activeClass = 'active'; Slideshow.call(this);
homepageSlider.container = '.section-teaser .featured-inner'; }
homepageSlider.init(); HeaderSlideshow.prototype = new Slideshow();
HeaderSlideshow.prototype.moveToItem = function (itemNumber) {
//Move the list of promo categories below the controls to allow all content to expand Slideshow.prototype.moveToItem.call(this, itemNumber);
$('.teaser-header').insertBefore(".slideshow-controls"); $('.section-teaser .teaser-header li').removeClass('selected');
$('.section-teaser .teaser-header li')
var headerListItems = $('.section-teaser .teaser-header li a'); .eq(itemNumber - 1)
.addClass('selected');
headerListItems.click(function() {
headerListItems.parent('li').removeClass('selected');
$(this).parent('li').addClass('selected');
homepageSlider.moveToItem(headerListItems.index(this) + 1);
homepageSlider.scroll = false;
return false;
});
return homepageSlider;
}; };
})(jQuery); var homepageSlider = new HeaderSlideshow();
homepageSlider.itemContainer = '.teaser-items';
homepageSlider.wrapperElement = 'div';
homepageSlider.wrapperClass = 'window';
homepageSlider.controlsMarkup =
'<p class="slideshow-controls">' +
'<a href="#" class="prev" rel="prev">Previous</a>' +
'<a href="#" class="next" rel="next">Next</a></p>';
homepageSlider.leftController = '.section-teaser a[rel="prev"]';
homepageSlider.rightController = '.section-teaser a[rel="next"]';
homepageSlider.activeClass = 'active';
homepageSlider.container = '.section-teaser .featured-inner';
homepageSlider.init();
jQuery(function($) { //Move the list of promo categories below the controls to allow all content to expand
// Greys out the favorites icon when it is clicked $('.teaser-header').insertBefore('.slideshow-controls');
$(".item-info li.favorite").click(function () {
var self = this; var headerListItems = $('.section-teaser .teaser-header li a');
$(self).addClass("favorite-loading");
setTimeout(function() { headerListItems.click(function () {
$(self).addClass("favorite-added"); headerListItems.parent('li').removeClass('selected');
},2000); $(this).parent('li').addClass('selected');
homepageSlider.moveToItem(headerListItems.index(this) + 1);
homepageSlider.scroll = false;
return false;
}); });
// account dropdown in auxillary menu return homepageSlider;
var accountDropdown = new DropdownArea(); };
// set up variables for object })(jQuery);
accountDropdown.trigger = ($('ul.account .controller')); // node
accountDropdown.target = ('ul.account ul'); // reference
accountDropdown.targetParent = ('ul.account'); // reference
accountDropdown.init();
// tools dropdown in auxillary menu jQuery(function ($) {
var toolsDropdown = new DropdownArea(); // Greys out the favorites icon when it is clicked
// set up variables for object $('.item-info li.favorite').click(function () {
toolsDropdown.trigger = ($('ul.tools .controller')); // node var self = this;
toolsDropdown.target = ('ul.tools ul'); // reference $(self).addClass('favorite-loading');
toolsDropdown.targetParent = ('ul.tools'); // reference setTimeout(function () {
toolsDropdown.init(); $(self).addClass('favorite-added');
}, 2000);
});
// change dropdown in auxillary menu // account dropdown in auxillary menu
var changeDropdown = new DropdownArea(); var accountDropdown = new DropdownArea();
// set up variables for object // set up variables for object
changeDropdown.trigger = ($('ul.change .controller')); // node accountDropdown.trigger = $('ul.account .controller'); // node
changeDropdown.target = ('ul.change ul'); // reference accountDropdown.target = 'ul.account ul'; // reference
changeDropdown.targetParent = ('ul.change'); // reference accountDropdown.targetParent = 'ul.account'; // reference
changeDropdown.init(); accountDropdown.init();
// notification dropdown // tools dropdown in auxillary menu
var notificationHelpDropdown = new DropdownArea(); var toolsDropdown = new DropdownArea();
// set up variables for object // set up variables for object
notificationHelpDropdown.trigger = ($('.notification .toggle-help')); // node toolsDropdown.trigger = $('ul.tools .controller'); // node
notificationHelpDropdown.target = ('.notification .toggle-info'); // reference toolsDropdown.target = 'ul.tools ul'; // reference
notificationHelpDropdown.targetParent = ('.notification'); // reference toolsDropdown.targetParent = 'ul.tools'; // reference
notificationHelpDropdown.init(); toolsDropdown.init();
$('.notification a.close').click(function() {
notificationHelpDropdown.hide(); // change dropdown in auxillary menu
return false; var changeDropdown = new DropdownArea();
}) // set up variables for object
changeDropdown.trigger = $('ul.change .controller'); // node
changeDropdown.target = 'ul.change ul'; // reference
changeDropdown.targetParent = 'ul.change'; // reference
changeDropdown.init();
// notification dropdown
var notificationHelpDropdown = new DropdownArea();
// set up variables for object
notificationHelpDropdown.trigger = $('.notification .toggle-help'); // node
notificationHelpDropdown.target = '.notification .toggle-info'; // reference
notificationHelpDropdown.targetParent = '.notification'; // reference
notificationHelpDropdown.init();
$('.notification a.close').click(function () {
notificationHelpDropdown.hide();
return false;
});
}); });
/* Initialization things that get run on every page. */ /* Initialization things that get run on every page. */
$(".hidden").hide(); // hide anything that should be hidden $('.hidden').hide(); // hide anything that should be hidden

Просмотреть файл

@ -2,48 +2,55 @@ z.visitor = z.Storage('visitor');
z.currentVisit = z.SessionStorage('current-visit'); z.currentVisit = z.SessionStorage('current-visit');
function initBanners() { function initBanners() {
var $body = $(document.body); var $body = $(document.body);
if ($body.hasClass('editor-tools')) { if ($body.hasClass('editor-tools')) {
// Don't bother showing those on editor tools, it has a bunch of weird // Don't bother showing those on editor tools, it has a bunch of weird
// styles for the menu that don't play nice with those banners. // styles for the menu that don't play nice with those banners.
return; return;
} }
// Show the various banners, but only one at a time, and only if they // Show the various banners, but only one at a time, and only if they
// haven't been dimissed before. // haven't been dimissed before.
// To reset dismissal state: z.visitor.remove('xx') // To reset dismissal state: z.visitor.remove('xx')
// Show the bad-browser message // Show the bad-browser message
if (!z.visitor.get('seen_badbrowser_warning') && $body.hasClass('badbrowser')) { if (
$('#site-nonfx').show(); !z.visitor.get('seen_badbrowser_warning') &&
} $body.hasClass('badbrowser')
// Show the first visit banner. ) {
else if (!z.visitor.get('seen_impala_first_visit')) { $('#site-nonfx').show();
$body.addClass('firstvisit'); }
z.visitor.set('seen_impala_first_visit', 1); // Show the first visit banner.
} else if (!z.visitor.get('seen_impala_first_visit')) {
// Show the link to try the new frontend (only on the homepage for now). $body.addClass('firstvisit');
else if (!z.visitor.get('seen_try_new_frontend') && $body.hasClass('home')) { z.visitor.set('seen_impala_first_visit', 1);
$('#try-new-frontend').show(); }
} // Show the link to try the new frontend (only on the homepage for now).
// Show the ACR pitch if it has not been dismissed. else if (!z.visitor.get('seen_try_new_frontend') && $body.hasClass('home')) {
else if (!z.visitor.get('seen_acr_pitch') && $body.hasClass('acr-pitch')) { $('#try-new-frontend').show();
$body.find('#acr-pitch').show(); }
} // Show the ACR pitch if it has not been dismissed.
else if (!z.visitor.get('seen_acr_pitch') && $body.hasClass('acr-pitch')) {
$body.find('#acr-pitch').show();
}
// Allow dismissal of site-balloons. // Allow dismissal of site-balloons.
$body.on('click', '.site-balloon .close, .site-tip .close', _pd(function() { $body.on(
var $parent = $(this).closest('.site-balloon, .site-tip'); 'click',
$parent.fadeOut(); '.site-balloon .close, .site-tip .close',
if ($parent.is('#site-nonfx')) { _pd(function () {
z.visitor.set('seen_badbrowser_warning', 1); var $parent = $(this).closest('.site-balloon, .site-tip');
} else if ($parent.is('#acr-pitch')) { $parent.fadeOut();
z.visitor.set('seen_acr_pitch', 1); if ($parent.is('#site-nonfx')) {
} else if ($parent.is('#appruntime-pitch')) { z.visitor.set('seen_badbrowser_warning', 1);
z.visitor.set('seen_appruntime_pitch', 1); } else if ($parent.is('#acr-pitch')) {
} else if ($parent.is('#try-new-frontend')) { z.visitor.set('seen_acr_pitch', 1);
z.visitor.set('seen_try_new_frontend', 1); } else if ($parent.is('#appruntime-pitch')) {
} z.visitor.set('seen_appruntime_pitch', 1);
})); } else if ($parent.is('#try-new-frontend')) {
z.visitor.set('seen_try_new_frontend', 1);
}
}),
);
} }

Просмотреть файл

@ -1,24 +1,24 @@
// Named key codes. That's the key idea here. // Named key codes. That's the key idea here.
z.keys = { z.keys = {
'SHIFT': 16, SHIFT: 16,
'CONTROL': 17, CONTROL: 17,
'ALT': 18, ALT: 18,
'PAUSE': 19, PAUSE: 19,
'CAPS_LOCK': 20, CAPS_LOCK: 20,
'ESCAPE': 27, ESCAPE: 27,
'ENTER': 13, ENTER: 13,
'PAGE_UP': 33, PAGE_UP: 33,
'PAGE_DOWN': 34, PAGE_DOWN: 34,
'LEFT': 37, LEFT: 37,
'UP': 38, UP: 38,
'RIGHT': 39, RIGHT: 39,
'DOWN': 40, DOWN: 40,
'HOME': 36, HOME: 36,
'END': 35, END: 35,
'COMMAND': 91, COMMAND: 91,
'WINDOWS_RIGHT': 92, WINDOWS_RIGHT: 92,
'COMMAND_RIGHT': 93, COMMAND_RIGHT: 93,
'WINDOWS_LEFT_OPERA': 219, WINDOWS_LEFT_OPERA: 219,
'WINDOWS_RIGHT_OPERA': 220, WINDOWS_RIGHT_OPERA: 220,
'APPLE': 24 APPLE: 24,
}; };

Просмотреть файл

@ -1,5 +1,9 @@
/* Remove "Go" buttons from <form class="go" */ /* Remove "Go" buttons from <form class="go" */
$(document).ready(function(){ $(document).ready(function () {
$('form.go').change(function() { this.submit(); }) $('form.go')
.find('button').hide(); .change(function () {
this.submit();
})
.find('button')
.hide();
}); });

Просмотреть файл

@ -1,61 +1,70 @@
// Replaces rating selectboxes with the rating widget // Replaces rating selectboxes with the rating widget
$.fn.ratingwidget = function(classes) { $.fn.ratingwidget = function (classes) {
this.each(function(n, el) { this.each(function (n, el) {
if (!classes) { if (!classes) {
classes = ''; classes = '';
}
var $el = $(el),
allClasses = 'ratingwidget stars stars-0 ' + classes,
$widget = $('<span class="' + allClasses + '"></span>'),
rs = '',
showStars = function (n) {
$widget
.removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5')
.addClass('stars-' + n);
},
setStars = function (n) {
if (rating == n) return;
var e = $widget.find(format('[value="{0}"]', n));
e.click();
showStars(n);
rating = n;
},
rating = null;
// Existing rating found so initialize the widget.
if ($('option[selected]', $el).length) {
var temp_rating = $el.val();
setStars(temp_rating);
rating = parseInt(temp_rating, 10);
}
for (var i = 1; i <= 5; i++) {
var checked = rating === i ? ' checked' : '';
rs += format(
'<label data-stars="{0}">{1}<input type="radio" name="rating"{2} value="{3}"></label>',
[i, format(ngettext('{0} star', '{0} stars', i), [i]), checked, i],
);
}
$widget
.click(function (evt) {
var t = $(evt.target);
if (t.is('input[type=radio]')) {
showStars((rating = t.val()));
if (!t.val()) {
// If the user caused a radio button to become unchecked,
// re-check it because that shouldn't happen.
t.prop('checked', true);
}
} }
var $el = $(el), })
allClasses = 'ratingwidget stars stars-0 ' + classes, .mouseover(function (evt) {
$widget = $('<span class="' + allClasses + '"></span>'), var t = $(evt.target);
rs = '', if (t.attr('data-stars')) {
showStars = function(n) { showStars(t.attr('data-stars'));
$widget.removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5').addClass('stars-' + n);
},
setStars = function(n) {
if (rating == n) return;
var e = $widget.find(format('[value="{0}"]', n));
e.click();
showStars(n);
rating = n;
},
rating = null;
// Existing rating found so initialize the widget.
if ($('option[selected]', $el).length) {
var temp_rating = $el.val();
setStars(temp_rating);
rating = parseInt(temp_rating, 10);
} }
for (var i=1; i<=5; i++) { })
var checked = rating === i ? ' checked' : ''; .mouseout(function () {
rs += format('<label data-stars="{0}">{1}<input type="radio" name="rating"{2} value="{3}"></label>', showStars(rating || 0);
[i, format(ngettext('{0} star', '{0} stars', i), [i]), checked, i]); })
} .on('touchmove touchend', function (e) {
$widget.click(function(evt) { var wid = $widget.width();
var t = $(evt.target); var left = $widget.offset().left;
if (t.is('input[type=radio]')) { var r =
showStars(rating = t.val()); ((e.originalEvent.changedTouches[0].clientX - left) / wid) * 5 + 1;
if (!t.val()) { r = ~~Math.min(Math.max(r, 1), 5);
// If the user caused a radio button to become unchecked, setStars(r);
// re-check it because that shouldn't happen. });
t.prop('checked', true); $widget.html(rs);
} $el.before($widget).detach();
} });
}).mouseover(function(evt) { return this;
var t = $(evt.target);
if (t.attr('data-stars')) {
showStars(t.attr('data-stars'));
}
}).mouseout(function() {
showStars(rating || 0);
}).on('touchmove touchend', function(e) {
var wid = $widget.width();
var left = $widget.offset().left;
var r = (e.originalEvent.changedTouches[0].clientX - left) / wid * 5 + 1;
r = ~~Math.min(Math.max(r,1),5);
setStars(r);
});
$widget.html(rs);
$el.before($widget).detach();
});
return this;
}; };

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -2,114 +2,138 @@
* extended by addonUploader(). Eventually imageUploader() should as well */ * extended by addonUploader(). Eventually imageUploader() should as well */
function fileSizeFormat(bytes) { function fileSizeFormat(bytes) {
var s = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; var s = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
if(bytes === 0) return bytes + " " + s[1]; if (bytes === 0) return bytes + ' ' + s[1];
var e = Math.floor( Math.log(bytes) / Math.log(1024) ); var e = Math.floor(Math.log(bytes) / Math.log(1024));
return (bytes / Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e]; return (bytes / Math.pow(1024, Math.floor(e))).toFixed(2) + ' ' + s[e];
} }
(function($) { (function ($) {
function getErrors(results) { function getErrors(results) {
return results.errors; return results.errors;
} }
var settings = {'filetypes': [], 'getErrors': getErrors, 'cancel': $(), 'maxSize': null}; var settings = {
filetypes: [],
getErrors: getErrors,
cancel: $(),
maxSize: null,
};
$.fn.fileUploader = function( options ) { $.fn.fileUploader = function (options) {
return $(this).each(function () {
var $upload_field = $(this),
formData = false,
errors = false,
aborted = false;
return $(this).each(function(){ if (options) {
var $upload_field = $(this), $.extend(settings, options);
formData = false, }
errors = false,
aborted = false;
if (options) { $upload_field.on('change', uploaderStart);
$.extend( settings, options );
}
$upload_field.on("change", uploaderStart); $(settings.cancel).click(
_pd(function () {
$upload_field.trigger('upload_action_abort');
}),
);
$(settings.cancel).click(_pd(function(){ function uploaderStart() {
$upload_field.trigger('upload_action_abort'); if ($upload_field[0].files.length === 0) {
})); return;
}
function uploaderStart() { var domfile = $upload_field[0].files[0],
if($upload_field[0].files.length === 0) { url = $upload_field.attr('data-upload-url'),
return; csrf = $('input[name=csrfmiddlewaretoken]').val(),
} file = {
name: domfile.name || domfile.fileName,
size: domfile.size,
type: domfile.type,
};
var domfile = $upload_field[0].files[0], formData = new z.FormData();
url = $upload_field.attr('data-upload-url'), aborted = false;
csrf = $("input[name=csrfmiddlewaretoken]").val(),
file = {'name': domfile.name || domfile.fileName,
'size': domfile.size,
'type': domfile.type};
formData = new z.FormData(); $upload_field.trigger('upload_start', [file]);
aborted = false;
$upload_field.trigger("upload_start", [file]); /* Disable uploading while something is uploading */
$upload_field.prop('disabled', true);
/* Disable uploading while something is uploading */ $upload_field.parent().find('a').addClass('disabled');
$upload_field.prop('disabled', true); $upload_field.on('reenable_uploader', function () {
$upload_field.parent().find('a').addClass("disabled"); $upload_field.prop('disabled', false);
$upload_field.on("reenable_uploader", function() { $upload_field.parent().find('a').removeClass('disabled');
$upload_field.prop('disabled', false);
$upload_field.parent().find('a').removeClass("disabled");
});
var exts = new RegExp("\\\.("+settings.filetypes.join('|')+")$", "i");
if(!file.name.match(exts)) {
errors = [gettext("The filetype you uploaded isn't recognized.")];
$upload_field.trigger("upload_errors", [file, errors]);
$upload_field.trigger("upload_finished", [file]);
return;
}
if (settings.maxSize && domfile.size > settings.maxSize) {
errors = [format(gettext("Your file exceeds the maximum size of {0}."), [fileSizeFormat(settings.maxSize)])];
$upload_field.trigger("upload_errors", [file, errors]);
$upload_field.trigger("upload_finished", [file]);
return;
}
// We should be good to go!
formData.open("POST", url, true);
formData.append("csrfmiddlewaretoken", csrf);
if(options.appendFormData) {
options.appendFormData(formData);
}
if(domfile instanceof File) { // Needed b/c of tests.
formData.append("upload", domfile);
}
$upload_field.off("upload_action_abort").on("upload_action_abort", function() {
aborted = true;
formData.xhr.abort();
errors = [gettext("You cancelled the upload.")];
$upload_field.trigger("upload_errors", [file, errors]);
$upload_field.trigger("upload_finished", [file]);
});
formData.xhr.upload.addEventListener("progress", function(e) {
if (e.lengthComputable) {
var pct = Math.round((e.loaded * 100) / e.total);
$upload_field.trigger("upload_progress", [file, pct]);
}
}, false);
formData.xhr.onreadystatechange = function(){
$upload_field.trigger("upload_onreadystatechange",
[file, formData.xhr, aborted]);
};
formData.send();
}
}); });
}; var exts = new RegExp(
'\\.(' + settings.filetypes.join('|') + ')$',
'i',
);
if (!file.name.match(exts)) {
errors = [gettext("The filetype you uploaded isn't recognized.")];
$upload_field.trigger('upload_errors', [file, errors]);
$upload_field.trigger('upload_finished', [file]);
return;
}
if (settings.maxSize && domfile.size > settings.maxSize) {
errors = [
format(gettext('Your file exceeds the maximum size of {0}.'), [
fileSizeFormat(settings.maxSize),
]),
];
$upload_field.trigger('upload_errors', [file, errors]);
$upload_field.trigger('upload_finished', [file]);
return;
}
// We should be good to go!
formData.open('POST', url, true);
formData.append('csrfmiddlewaretoken', csrf);
if (options.appendFormData) {
options.appendFormData(formData);
}
if (domfile instanceof File) {
// Needed b/c of tests.
formData.append('upload', domfile);
}
$upload_field
.off('upload_action_abort')
.on('upload_action_abort', function () {
aborted = true;
formData.xhr.abort();
errors = [gettext('You cancelled the upload.')];
$upload_field.trigger('upload_errors', [file, errors]);
$upload_field.trigger('upload_finished', [file]);
});
formData.xhr.upload.addEventListener(
'progress',
function (e) {
if (e.lengthComputable) {
var pct = Math.round((e.loaded * 100) / e.total);
$upload_field.trigger('upload_progress', [file, pct]);
}
},
false,
);
formData.xhr.onreadystatechange = function () {
$upload_field.trigger('upload_onreadystatechange', [
file,
formData.xhr,
aborted,
]);
};
formData.send();
}
});
};
})(jQuery); })(jQuery);

Просмотреть файл

@ -34,117 +34,122 @@
file in the upload box when the "onchange" event is fired.] file in the upload box when the "onchange" event is fired.]
*/ */
// Get an object URL across browsers. // Get an object URL across browsers.
$.fn.objectUrl = function(offset) { $.fn.objectUrl = function (offset) {
var files = $(this)[0].files, var files = $(this)[0].files,
url = false; url = false;
if (z.capabilities.fileAPI && files.length) { if (z.capabilities.fileAPI && files.length) {
offset = offset || 0; offset = offset || 0;
var f = files[offset]; var f = files[offset];
if (typeof window.URL !== 'undefined') { if (typeof window.URL !== 'undefined') {
url = window.URL.createObjectURL(f); url = window.URL.createObjectURL(f);
} else if (typeof window.webkitURL == 'function') { } else if (typeof window.webkitURL == 'function') {
url = window.webkitURL.createObjectURL(f); url = window.webkitURL.createObjectURL(f);
} else if(typeof f.getAsDataURL == 'function') { } else if (typeof f.getAsDataURL == 'function') {
url = f.getAsDataURL(); url = f.getAsDataURL();
}
} }
return url; }
return url;
}; };
(function ($) {
var instance_id = 0;
(function($) { $.fn.imageUploader = function () {
var instance_id = 0; var $upload_field = this,
outstanding_uploads = 0,
files = $upload_field[0].files,
url = $upload_field.attr('data-upload-url'),
csrf = $upload_field.closest('form').find('input[name^=csrf]').val();
$.fn.imageUploader = function() { // No files? No API support? No shirt? No service.
var $upload_field = this, if (!z.capabilities.fileAPI || files.length === 0) {
outstanding_uploads = 0, return false;
files = $upload_field[0].files, }
url = $upload_field.attr('data-upload-url'),
csrf = $upload_field.closest('form').find('input[name^=csrf]').val();
// No files? No API support? No shirt? No service. $upload_field.trigger('upload_start_all');
if (!z.capabilities.fileAPI || files.length === 0) {
return false; // Loop through the files.
$.each(files, function (v, f) {
var data = '',
file = {
instance: instance_id,
name: f.name || f.fileName,
size: f.size,
type: f.type,
aborted: false,
dataURL: false,
},
finished = function () {
outstanding_uploads--;
if (outstanding_uploads <= 0) {
$upload_field.trigger('upload_finished_all');
}
$upload_field.trigger('upload_finished', [file]);
},
formData = new z.FormData();
instance_id++;
outstanding_uploads++;
if (
$upload_field.attr('data-allowed-types').split('|').indexOf(file.type) <
0
) {
var errors = [gettext('Images must be either PNG or JPG.')];
if (typeof $upload_field.attr('multiple') !== 'undefined') {
// If we have a `multiple` attribute, assume not an icon.
if ($upload_field.attr('data-allowed-types').indexOf('video') > -1) {
errors.push([gettext('Videos must be in WebM.')]);
}
} }
$upload_field.trigger('upload_start', [file]);
$upload_field.trigger('upload_errors', [file, errors]);
finished();
return;
}
$upload_field.trigger("upload_start_all"); file.dataURL = $upload_field.objectUrl(v);
// Loop through the files. // And we're off!
$.each(files, function(v, f){ $upload_field.trigger('upload_start', [file]);
var data = "",
file = {
'instance': instance_id,
'name': f.name || f.fileName,
'size': f.size,
'type': f.type,
'aborted': false,
'dataURL': false},
finished = function(){
outstanding_uploads--;
if(outstanding_uploads <= 0) {
$upload_field.trigger("upload_finished_all");
}
$upload_field.trigger("upload_finished", [file]);
},
formData = new z.FormData();
instance_id++; // Set things up
outstanding_uploads++; formData.open('POST', url, true);
formData.append('csrfmiddlewaretoken', csrf);
formData.append('upload_image', f);
if ($upload_field.attr('data-allowed-types').split('|').indexOf(file.type) < 0) { // Monitor progress and report back.
var errors = [gettext('Images must be either PNG or JPG.')]; formData.xhr.onreadystatechange = function () {
if (typeof $upload_field.attr('multiple') !== 'undefined') { if (
// If we have a `multiple` attribute, assume not an icon. formData.xhr.readyState == 4 &&
if ($upload_field.attr('data-allowed-types').indexOf('video') > -1) { formData.xhr.responseText &&
errors.push([gettext('Videos must be in WebM.')]); (formData.xhr.status == 200 || formData.xhr.status == 304)
} ) {
} var json = {};
$upload_field.trigger('upload_start', [file]); try {
$upload_field.trigger('upload_errors', [file, errors]); json = JSON.parse(formData.xhr.responseText);
finished(); } catch (err) {
return; var error = gettext('There was a problem contacting the server.');
} $upload_field.trigger('upload_errors', [file, error]);
finished();
return false;
}
file.dataURL = $upload_field.objectUrl(v); if (json.errors.length) {
$upload_field.trigger('upload_errors', [file, json.errors]);
} else {
$upload_field.trigger('upload_success', [file, json.upload_hash]);
}
finished();
}
};
// And we're off! // Actually do the sending.
$upload_field.trigger("upload_start", [file]); formData.send();
});
// Set things up // Clear out images, since we uploaded them.
formData.open("POST", url, true); $upload_field.val('');
formData.append("csrfmiddlewaretoken", csrf); };
formData.append("upload_image", f);
// Monitor progress and report back.
formData.xhr.onreadystatechange = function(){
if (formData.xhr.readyState == 4 && formData.xhr.responseText &&
(formData.xhr.status == 200 || formData.xhr.status == 304)) {
var json = {};
try {
json = JSON.parse(formData.xhr.responseText);
} catch(err) {
var error = gettext("There was a problem contacting the server.");
$upload_field.trigger("upload_errors", [file, error]);
finished();
return false;
}
if(json.errors.length) {
$upload_field.trigger("upload_errors", [file, json.errors]);
} else {
$upload_field.trigger("upload_success", [file, json.upload_hash]);
}
finished();
}
};
// Actually do the sending.
formData.send();
});
// Clear out images, since we uploaded them.
$upload_field.val("");
};
})(jQuery); })(jQuery);

Просмотреть файл

@ -1,5 +1,4 @@
if(document.getElementById('live_refresh')) { if (document.getElementById('live_refresh')) {
less.env = "development"; less.env = 'development';
less.watch(); less.watch();
} }

Просмотреть файл

@ -1,6 +1,6 @@
var links = document.getElementsByTagName('link'); var links = document.getElementsByTagName('link');
for (var i = 0; i < links.length; i++) { for (var i = 0; i < links.length; i++) {
if (/\.less($|\?)/.test(links[i].href)) { if (/\.less($|\?)/.test(links[i].href)) {
links[i].type = "text/x-less"; links[i].type = 'text/x-less';
} }
} }

Просмотреть файл

@ -1,10 +1,12 @@
$(function() { $(function () {
var $abuse = $('fieldset.abuse'); var $abuse = $('fieldset.abuse');
if ($abuse.find('legend a').length) { if ($abuse.find('legend a').length) {
var $ol = $abuse.find('ol'); var $ol = $abuse.find('ol');
$ol.hide(); $ol.hide();
$abuse.find('legend a, .cancel').click(_pd(function() { $abuse.find('legend a, .cancel').click(
$ol.slideToggle('fast'); _pd(function () {
})); $ol.slideToggle('fast');
} }),
);
}
}); });

Просмотреть файл

@ -1,166 +1,178 @@
$(function () { $(function () {
if (!$("body").hasClass('addon-details')) return; if (!$('body').hasClass('addon-details')) return;
if ($("body.restyle").length === 1) { if ($('body.restyle').length === 1) {
$('#background-wrapper').height( $('#background-wrapper').height(
$('.amo-header').height() + $('.amo-header').height() +
($('.notification-box').length ? 80 : 0) + ($('.notification-box').length ? 80 : 0) +
$('.addon-description-header').height() + 20 $('.addon-description-header').height() +
); 20,
);
}
$('.previews').zCarousel({
btnNext: '.previews .next',
btnPrev: '.previews .prev',
itemsPerPage: 3,
});
(function () {
var $document = $(document),
$lightbox = $('#lightbox'),
$content = $('#lightbox .content'),
$caption = $('#lightbox .caption span'),
$previews = $('.previews'),
current,
$strip,
lbImage = template('<img id="preview{0}" src="{1}" alt="">');
if (!$lightbox.length) return;
function showLightbox() {
$lightbox.show();
showImage(this);
$(window).on('keydown.lightboxDismiss', function (e) {
switch (e.which) {
case z.keys.ESCAPE:
e.preventDefault();
hideLightbox();
break;
case z.keys.LEFT:
e.preventDefault();
showPrev();
break;
case z.keys.RIGHT:
e.preventDefault();
showNext();
break;
}
});
//I want to ensure the lightbox is painted before fading it in.
setTimeout(function () {
$lightbox.addClass('show');
}, 0);
} }
function hideLightbox() {
$(".previews").zCarousel({ $lightbox.removeClass('show');
btnNext: ".previews .next", // We can't trust transitionend to fire in all cases.
btnPrev: ".previews .prev", setTimeout(function () {
itemsPerPage: 3 $lightbox.hide();
}); }, 500);
(function() { $(window).off('keydown.lightboxDismiss');
var $document = $(document), }
$lightbox = $("#lightbox"), function showImage(a) {
$content = $("#lightbox .content"), var $a = $(a),
$caption = $("#lightbox .caption span"), $oldimg = $lightbox.find('img');
$previews = $('.previews'), current = $a.parent().index();
current, $strip, $strip = $a.closest('ul').find('li');
lbImage = template('<img id="preview{0}" src="{1}" alt="">'); $previews
if (!$lightbox.length) return; .find('.panel')
function showLightbox() { .removeClass('active')
$lightbox.show(); .eq(current)
showImage(this); .addClass('active');
$(window).on('keydown.lightboxDismiss', function(e) { var $img = $('#preview' + current);
switch(e.which) { if ($img.length) {
case z.keys.ESCAPE: $oldimg.css({ opacity: '0', 'z-index': '0' });
e.preventDefault(); $img.css({
hideLightbox(); opacity: '1',
break; 'z-index': '1',
case z.keys.LEFT:
e.preventDefault();
showPrev();
break;
case z.keys.RIGHT:
e.preventDefault();
showNext();
break;
}
});
//I want to ensure the lightbox is painted before fading it in.
setTimeout(function () {
$lightbox.addClass("show");
},0);
}
function hideLightbox() {
$lightbox.removeClass("show");
// We can't trust transitionend to fire in all cases.
setTimeout(function() {
$lightbox.hide();
}, 500);
$(window).off('keydown.lightboxDismiss');
}
function showImage(a) {
var $a = $(a),
$oldimg = $lightbox.find("img");
current = $a.parent().index();
$strip = $a.closest("ul").find("li");
$previews.find('.panel').removeClass('active')
.eq(current).addClass('active');
var $img = $("#preview"+current);
if ($img.length) {
$oldimg.css({"opacity": "0", "z-index": "0"});
$img.css({
"opacity": "1", "z-index": "1"
});
} else {
$img = $(lbImage([current, $a.attr("href")]));
$content.append($img);
$img.on("load", function(e) {
$oldimg.css({"opacity": "0", "z-index": "0"});
$img.css({
"opacity": "1", "z-index": "1"
});
for (var i=0; i<$strip.length; i++) {
if (i != current) {
var $p = $strip.eq(i).find("a");
$content.append(lbImage([i, $p.attr("href")]));
}
}
});
}
$caption.text($a.attr("title"));
$lightbox.find(".control").removeClass("disabled");
if (current < 1) {
$lightbox.find(".control.prev").addClass("disabled");
}
if (current == $strip.length-1){
$lightbox.find(".control.next").addClass("disabled");
}
}
function showNext() {
if (current < $strip.length-1) {
showImage($strip.eq(current+1).find("a"));
if (!this.window) {
$(this).blur();
}
}
}
function showPrev() {
if (current > 0) {
showImage($strip.eq(current-1).find("a"));
if (!this.window) {
$(this).blur();
}
}
}
$("#lightbox .next").click(_pd(showNext));
$("#lightbox .prev").click(_pd(showPrev));
$(".previews ul a").click(_pd(showLightbox));
$('#lightbox').click(_pd(function(e) {
if ($(e.target).is('.close, #lightbox')) {
hideLightbox();
}
}));
})();
if ($('#more-webpage').exists()) {
var $moreEl = $('#more-webpage');
url = $moreEl.attr('data-more-url');
$.get(url, function(resp) {
var $document = $(document);
var scrollTop = $document.scrollTop();
var origHeight = $document.height();
// We need to correct scrolling position if the user scrolled down
// already (e.g. by using a link with anchor). This correction is
// only necessary if the scrolling position is below the element we
// replace or the user scrolled down to the bottom of the document.
var shouldCorrectScrolling = scrollTop > $moreEl.offset().top;
if (scrollTop && scrollTop >= origHeight - $(window).height()) {
shouldCorrectScrolling = true;
}
// Strip the leading whitespace so that $() treats this as html and
// not a selector.
var $newContent = $(resp.trim());
$moreEl.replaceWith($newContent);
$newContent.find('.listing-grid h3').truncate( {dir: 'h'} );
$newContent.find('.install').installButton();
$newContent.find('.listing-grid').each(listing_grid);
$('#reviews-link').addClass('scrollto').attr('href', '#reviews');
if (shouldCorrectScrolling) {
// User scrolled down already, adjust scrolling position so
// that the same content stays visible.
var heightDifference = $document.height() - origHeight;
$document.scrollTop(scrollTop + heightDifference);
}
}); });
} else {
$img = $(lbImage([current, $a.attr('href')]));
$content.append($img);
$img.on('load', function (e) {
$oldimg.css({ opacity: '0', 'z-index': '0' });
$img.css({
opacity: '1',
'z-index': '1',
});
for (var i = 0; i < $strip.length; i++) {
if (i != current) {
var $p = $strip.eq(i).find('a');
$content.append(lbImage([i, $p.attr('href')]));
}
}
});
}
$caption.text($a.attr('title'));
$lightbox.find('.control').removeClass('disabled');
if (current < 1) {
$lightbox.find('.control.prev').addClass('disabled');
}
if (current == $strip.length - 1) {
$lightbox.find('.control.next').addClass('disabled');
}
} }
function showNext() {
if (current < $strip.length - 1) {
showImage($strip.eq(current + 1).find('a'));
if (!this.window) {
$(this).blur();
}
}
}
function showPrev() {
if (current > 0) {
showImage($strip.eq(current - 1).find('a'));
if (!this.window) {
$(this).blur();
}
}
}
$('#lightbox .next').click(_pd(showNext));
$('#lightbox .prev').click(_pd(showPrev));
$('.previews ul a').click(_pd(showLightbox));
$('#lightbox').click(
_pd(function (e) {
if ($(e.target).is('.close, #lightbox')) {
hideLightbox();
}
}),
);
})();
if ($('#review-add-box').exists()) if ($('#more-webpage').exists()) {
$('#review-add-box').modal('#add-review', { delegate: '#page', width: '650px' }); var $moreEl = $('#more-webpage');
url = $moreEl.attr('data-more-url');
$.get(url, function (resp) {
var $document = $(document);
var scrollTop = $document.scrollTop();
var origHeight = $document.height();
if ($('#privacy-policy').exists()) // We need to correct scrolling position if the user scrolled down
$('#privacy-policy').modal('.privacy-policy', { width: '500px' }); // already (e.g. by using a link with anchor). This correction is
if ($('#webext-permissions').exists()) // only necessary if the scrolling position is below the element we
$('#webext-permissions').modal('.webext-permissions', { width: '500px' }); // replace or the user scrolled down to the bottom of the document.
var shouldCorrectScrolling = scrollTop > $moreEl.offset().top;
if (scrollTop && scrollTop >= origHeight - $(window).height()) {
shouldCorrectScrolling = true;
}
$('#abuse-modal').modal('#report-abuse', {delegate: '#page'}); // Strip the leading whitespace so that $() treats this as html and
// not a selector.
var $newContent = $(resp.trim());
$moreEl.replaceWith($newContent);
$newContent.find('.listing-grid h3').truncate({ dir: 'h' });
$newContent.find('.install').installButton();
$newContent.find('.listing-grid').each(listing_grid);
$('#reviews-link').addClass('scrollto').attr('href', '#reviews');
if (shouldCorrectScrolling) {
// User scrolled down already, adjust scrolling position so
// that the same content stays visible.
var heightDifference = $document.height() - origHeight;
$document.scrollTop(scrollTop + heightDifference);
}
});
}
if ($('#review-add-box').exists())
$('#review-add-box').modal('#add-review', {
delegate: '#page',
width: '650px',
});
if ($('#privacy-policy').exists())
$('#privacy-policy').modal('.privacy-policy', { width: '500px' });
if ($('#webext-permissions').exists())
$('#webext-permissions').modal('.webext-permissions', { width: '500px' });
$('#abuse-modal').modal('#report-abuse', { delegate: '#page' });
}); });

Просмотреть файл

@ -1,98 +1,97 @@
function objEqual(a, b) { function objEqual(a, b) {
return JSON.stringify(a) == JSON.stringify(b); return JSON.stringify(a) == JSON.stringify(b);
} }
z._AjaxCache = {}; z._AjaxCache = {};
z.AjaxCache = (function() { z.AjaxCache = (function () {
return function(namespace) { return function (namespace) {
if (z._AjaxCache[namespace] === undefined) { if (z._AjaxCache[namespace] === undefined) {
z._AjaxCache[namespace] = { z._AjaxCache[namespace] = {
'previous': {'args': '', 'data': ''}, previous: { args: '', data: '' },
'items': {} items: {},
}; };
} }
return z._AjaxCache[namespace]; return z._AjaxCache[namespace];
}; };
})(); })();
(function ($) {
(function($) { $.ajaxCache = function (o) {
o = $.extend(
$.ajaxCache = function(o) { {
o = $.extend({
url: '', url: '',
type: 'get', type: 'get',
data: {}, // Key/value pairs of form data. data: {}, // Key/value pairs of form data.
newItems: $.noop, // Callback upon success of items fetched. newItems: $.noop, // Callback upon success of items fetched.
cacheSuccess: $.noop, // Callback upon success of items fetched cacheSuccess: $.noop, // Callback upon success of items fetched
// in cache. // in cache.
ajaxSuccess: $.noop, // Callback upon success of Ajax request. ajaxSuccess: $.noop, // Callback upon success of Ajax request.
ajaxFailure: $.noop // Callback upon failure of Ajax request. ajaxFailure: $.noop, // Callback upon failure of Ajax request.
}, o); },
o,
);
if (!z.capabilities.JSON || parseFloat(jQuery.fn.jquery) < 1.5) { if (!z.capabilities.JSON || parseFloat(jQuery.fn.jquery) < 1.5) {
// jqXHR objects allow Deferred methods as of jQuery 1.5. Some of our // jqXHR objects allow Deferred methods as of jQuery 1.5. Some of our
// old pages are stuck on jQuery 1.4, so hopefully this'll disappear // old pages are stuck on jQuery 1.4, so hopefully this'll disappear
// sooner than later. // sooner than later.
return $.ajax({ return $.ajax({
url: o.url, url: o.url,
type: o.method, type: o.method,
data: o.data, data: o.data,
success: function(data) { success: function (data) {
o.newItems(data, data); o.newItems(data, data);
o.ajaxSuccess(data, items); o.ajaxSuccess(data, items);
}, },
errors: function(data) { errors: function (data) {
o.ajaxFailure(data); o.ajaxFailure(data);
} },
}); });
} }
var cache = z.AjaxCache(o.url + ':' + o.type), var cache = z.AjaxCache(o.url + ':' + o.type),
args = JSON.stringify(o.data), args = JSON.stringify(o.data),
previous_args = JSON.stringify(cache.previous.args), previous_args = JSON.stringify(cache.previous.args),
items, items,
request; request;
if (args != previous_args) { if (args != previous_args) {
if (!!cache.items[args]) { if (!!cache.items[args]) {
items = cache.items[args]; items = cache.items[args];
if (o.newItems) { if (o.newItems) {
o.newItems(null, items); o.newItems(null, items);
}
if (o.cacheSuccess) {
o.cacheSuccess(null, items);
}
} else {
// Make a request to fetch new items.
request = $.ajax({url: o.url, type: o.method, data: o.data});
request.done(function(data) {
var items;
if (!objEqual(data, cache.previous.data)) {
items = data;
}
o.newItems(data, items);
o.ajaxSuccess(data, items);
// Store items returned from this request.
cache.items[args] = data;
// Store current list of items and form data (arguments).
cache.previous.data = data;
cache.previous.args = args;
});
// Optional failure callback.
if (o.failure) {
request.fail(function(data) {
o.ajaxFailure(data);
});
}
} }
if (o.cacheSuccess) {
o.cacheSuccess(null, items);
}
} else {
// Make a request to fetch new items.
request = $.ajax({ url: o.url, type: o.method, data: o.data });
request.done(function (data) {
var items;
if (!objEqual(data, cache.previous.data)) {
items = data;
}
o.newItems(data, items);
o.ajaxSuccess(data, items);
// Store items returned from this request.
cache.items[args] = data;
// Store current list of items and form data (arguments).
cache.previous.data = data;
cache.previous.args = args;
});
// Optional failure callback.
if (o.failure) {
request.fail(function (data) {
o.ajaxFailure(data);
});
}
}
} }
return request; return request;
}; };
})(jQuery); })(jQuery);

Просмотреть файл

@ -1,28 +1,30 @@
z.capabilities = { z.capabilities = {
'JSON': window.JSON && typeof JSON.parse == 'function', JSON: window.JSON && typeof JSON.parse == 'function',
'debug': (('' + document.location).indexOf('dbg') >= 0), debug: ('' + document.location).indexOf('dbg') >= 0,
'debug_in_page': (('' + document.location).indexOf('dbginpage') >= 0), debug_in_page: ('' + document.location).indexOf('dbginpage') >= 0,
'console': window.console && (typeof window.console.log == 'function'), console: window.console && typeof window.console.log == 'function',
'replaceState': typeof history.replaceState === 'function', replaceState: typeof history.replaceState === 'function',
'chromeless': window.locationbar && !window.locationbar.visible, chromeless: window.locationbar && !window.locationbar.visible,
'localStorage': false, localStorage: false,
'sessionStorage': false, sessionStorage: false,
'fileAPI': !!window.FileReader, fileAPI: !!window.FileReader,
'performance': !!(window.performance || window.msPerformance || window.webkitPerformance || window.mozPerformance), performance: !!(
'webactivities': !!(window.setMessageHandler || window.mozSetMessageHandler) window.performance ||
window.msPerformance ||
window.webkitPerformance ||
window.mozPerformance
),
webactivities: !!(window.setMessageHandler || window.mozSetMessageHandler),
}; };
try {
if ('localStorage' in window && window['localStorage'] !== null) {
z.capabilities.localStorage = true;
}
} catch (e) {}
try { try {
if ('localStorage' in window && window['localStorage'] !== null) { if ('sessionStorage' in window && window['sessionStorage'] !== null) {
z.capabilities.localStorage = true; z.capabilities.sessionStorage = true;
} }
} catch (e) { } catch (e) {}
}
try {
if ('sessionStorage' in window && window['sessionStorage'] !== null) {
z.capabilities.sessionStorage = true;
}
} catch (e) {
}

Просмотреть файл

@ -5,49 +5,52 @@
* handles fluid layouts like a champ! * handles fluid layouts like a champ!
*/ */
(function($) { (function ($) {
$.fn.zCarousel = function (o) {
$.fn.zCarousel = function(o) { o = $.extend(
o = $.extend({ {
itemsPerPage: 1, itemsPerPage: 1,
circular: false circular: false,
}, o); },
o,
);
var $self = $(this).eq(0), var $self = $(this).eq(0),
$strip = $(".slider", $self), $strip = $('.slider', $self),
$lis = $strip.find(".panel"), $lis = $strip.find('.panel'),
$prev = $(o.btnPrev), $prev = $(o.btnPrev),
$next = $(o.btnNext), $next = $(o.btnNext),
prop = o.prop || ($("body").hasClass("html-rtl") ? "right" : "left"), prop = o.prop || ($('body').hasClass('html-rtl') ? 'right' : 'left'),
currentPos = 0, currentPos = 0,
maxPos = Math.ceil($lis.length / o.itemsPerPage); maxPos = Math.ceil($lis.length / o.itemsPerPage);
if (!$strip.length) return $self; if (!$strip.length) return $self;
function render(pos) { function render(pos) {
if (o.circular) { if (o.circular) {
currentPos = pos > maxPos+1 ? pos-maxPos : (pos < 0 ? pos+maxPos : pos); currentPos =
if ($strip.hasClass("noslide")) { pos > maxPos + 1 ? pos - maxPos : pos < 0 ? pos + maxPos : pos;
currentPos = (pos > maxPos) ? 1 : (pos < 1 ? maxPos : pos); if ($strip.hasClass('noslide')) {
} currentPos = pos > maxPos ? 1 : pos < 1 ? maxPos : pos;
} else {
currentPos = Math.min(Math.max(0, pos), maxPos-1);
} }
$strip.css(prop, currentPos * -100 + "%"); } else {
$prev.toggleClass("disabled", currentPos == 0 && !o.circular); currentPos = Math.min(Math.max(0, pos), maxPos - 1);
$next.toggleClass("disabled", currentPos == maxPos-1 && !o.circular); }
//wait for paint to clear the class. lame. $strip.css(prop, currentPos * -100 + '%');
setTimeout(function() { $prev.toggleClass('disabled', currentPos == 0 && !o.circular);
$strip.removeClass('noslide'); $next.toggleClass('disabled', currentPos == maxPos - 1 && !o.circular);
}, 0); //wait for paint to clear the class. lame.
setTimeout(function () {
$strip.removeClass('noslide');
}, 0);
} }
//wire up controls. //wire up controls.
function fwd() { function fwd() {
render(currentPos+1); render(currentPos + 1);
} }
function prev() { function prev() {
render(currentPos-1); render(currentPos - 1);
} }
$next.click(_pd(fwd)); $next.click(_pd(fwd));
$prev.click(_pd(prev)); $prev.click(_pd(prev));
@ -56,30 +59,30 @@ $.fn.zCarousel = function(o) {
// Strip text nodes so inline-block works properly. // Strip text nodes so inline-block works properly.
var cn = $strip[0].childNodes; var cn = $strip[0].childNodes;
for(var i = 0; i < cn.length; i++) { for (var i = 0; i < cn.length; i++) {
if (cn[i].nodeType == 3) { if (cn[i].nodeType == 3) {
$strip[0].removeChild(cn[i]); $strip[0].removeChild(cn[i]);
}; }
} }
if (o.circular) { if (o.circular) {
//pad the beginning with a page from the end vice-versa. //pad the beginning with a page from the end vice-versa.
$strip.prepend($lis.slice(-o.itemsPerPage).clone().addClass("cloned")) $strip
.append($lis.slice(0,o.itemsPerPage).clone().addClass("cloned")); .prepend($lis.slice(-o.itemsPerPage).clone().addClass('cloned'))
$strip.addClass('noslide'); .append($lis.slice(0, o.itemsPerPage).clone().addClass('cloned'));
$strip.on("transitionend webkitTransitionEnd", function() { $strip.addClass('noslide');
if (currentPos > maxPos || currentPos < 1) { $strip.on('transitionend webkitTransitionEnd', function () {
$strip.addClass("noslide"); if (currentPos > maxPos || currentPos < 1) {
setTimeout(function() { $strip.addClass('noslide');
render(currentPos); setTimeout(function () {
}, 0); render(currentPos);
} }, 0);
}); }
render(o.itemsPerPage); });
render(o.itemsPerPage);
} else { } else {
render(0); render(0);
} }
return $self; return $self;
}; };
})(jQuery); })(jQuery);

Просмотреть файл

@ -1,15 +1,18 @@
(function() { (function () {
var $footer = $('#footer'), var $footer = $('#footer'),
$page = $('#page'), $page = $('#page'),
$win = $(window); $win = $(window);
function stickyFooter() { function stickyFooter() {
// Stick the footer to the bottom when there's head(foot)room. // Stick the footer to the bottom when there's head(foot)room.
$footer.toggleClass('sticky', $win.height() - $footer.outerHeight() > $page.outerHeight()); $footer.toggleClass(
} 'sticky',
stickyFooter(); $win.height() - $footer.outerHeight() > $page.outerHeight(),
$win.resize(_.debounce(stickyFooter, 200)); );
}
stickyFooter();
$win.resize(_.debounce(stickyFooter, 200));
})(); })();
$(document).ready(function() { $(document).ready(function () {
$(window).trigger('resize'); $(window).trigger('resize');
}); });

Просмотреть файл

@ -1,31 +1,27 @@
function clearErrors(context) { function clearErrors(context) {
$('.errorlist', context).remove(); $('.errorlist', context).remove();
$('.error', context).removeClass('error'); $('.error', context).removeClass('error');
} }
function populateErrors(context, o) { function populateErrors(context, o) {
clearErrors(context); clearErrors(context);
var $list = $('<ul class="errorlist"></ul>'); var $list = $('<ul class="errorlist"></ul>');
$.each(o, function(i, v) { $.each(o, function (i, v) {
var $row = $('[name=' + i + ']', context).closest('.row'); var $row = $('[name=' + i + ']', context).closest('.row');
$row.addClass('error'); $row.addClass('error');
$row.append($list.append($(format('<li>{0}</li>', _.escape(v))))); $row.append($list.append($(format('<li>{0}</li>', _.escape(v)))));
}); });
} }
function fieldFocused(e) { function fieldFocused(e) {
var tags = /input|keygen|meter|option|output|progress|select|textarea/i; var tags = /input|keygen|meter|option|output|progress|select|textarea/i;
return tags.test(e.target.nodeName); return tags.test(e.target.nodeName);
} }
function postUnsaved(data) { function postUnsaved(data) {
$('input[name="unsaved_data"]').val(JSON.stringify(data)); $('input[name="unsaved_data"]').val(JSON.stringify(data));
} }
function loadUnsaved() { function loadUnsaved() {
return JSON.parse($('input[name="unsaved_data"]').val() || '{}'); return JSON.parse($('input[name="unsaved_data"]').val() || '{}');
} }

Просмотреть файл

@ -1,232 +1,245 @@
function updateTotalForms(prefix, inc) { function updateTotalForms(prefix, inc) {
var $totalForms = $('#id_' + prefix + '-TOTAL_FORMS'), var $totalForms = $('#id_' + prefix + '-TOTAL_FORMS'),
$maxForms = $('#id_' + prefix + '-MAX_NUM_FORMS'), $maxForms = $('#id_' + prefix + '-MAX_NUM_FORMS'),
inc = inc || 1, inc = inc || 1,
num = parseInt($totalForms.val(), 10) + inc; num = parseInt($totalForms.val(), 10) + inc;
if ($maxForms.length && $maxForms.val().length) { if ($maxForms.length && $maxForms.val().length) {
var maxNum = parseInt($maxForms.val(), 10); var maxNum = parseInt($maxForms.val(), 10);
if (num > maxNum) { if (num > maxNum) {
return num - 1; return num - 1;
}
} }
$totalForms.val(num); }
return num; $totalForms.val(num);
return num;
} }
/** /**
* zAutoFormset: handles Django formsets with autocompletion like a champ! * zAutoFormset: handles Django formsets with autocompletion like a champ!
* by cvan * by cvan
*/ */
(function($) { (function ($) {
$.zAutoFormset = function (o) {
o = $.extend(
{
delegate: document.body, // Delegate (probably some nearby parent).
$.zAutoFormset = function(o) { forms: null, // Where all the forms live (maybe a <ul>).
o = $.extend({ extraForm: '.extra-form', // Selector for element that contains the
delegate: document.body, // Delegate (probably some nearby parent). // HTML for extra-form template.
forms: null, // Where all the forms live (maybe a <ul>). maxForms: 3, // Maximum number of forms allowed.
extraForm: '.extra-form', // Selector for element that contains the prefix: 'form', // Formset prefix (Django default: 'form').
// HTML for extra-form template.
maxForms: 3, // Maximum number of forms allowed. hiddenField: null, // This is the name of a (hidden) field
// that will contain the value of the
// formPK for each newly added form.
prefix: 'form', // Formset prefix (Django default: 'form'). removeClass: 'remove', // Class for button triggering form removal.
hiddenField: null, // This is the name of a (hidden) field formSelector: 'li', // Selector for each form container.
// that will contain the value of the
// formPK for each newly added form.
removeClass: 'remove', // Class for button triggering form removal. formPK: 'id', // Primary key for initial forms.
formSelector: 'li', // Selector for each form container. src: null, // Source URL of JSON search results.
formPK: 'id', // Primary key for initial forms. input: null, // Input field for autocompletion search.
src: null, // Source URL of JSON search results. searchField: 'q', // Name of form field for search query.
input: null, // Input field for autocompletion search. minSearchLength: 3, // Minimum character length for queries.
searchField: 'q', // Name of form field for search query. width: 300, // Width (pixels) of autocomplete dropdown.
minSearchLength: 3, // Minimum character length for queries. addedCB: null, // Callback for each new form added.
width: 300, // Width (pixels) of autocomplete dropdown. removedCB: null, // Callback for each form removed.
addedCB: null, // Callback for each new form added. autocomplete: null, // Custom handler you can provide to handle
// autocompletion yourself.
removedCB: null, // Callback for each form removed. },
o,
autocomplete: null // Custom handler you can provide to handle );
// autocompletion yourself.
}, o);
var $delegate = $(o.delegate), var $delegate = $(o.delegate),
$forms = o.forms ? $delegate.find(o.forms) : $delegate, $forms = o.forms ? $delegate.find(o.forms) : $delegate,
$extraForm = $delegate.find(o.extraForm), $extraForm = $delegate.find(o.extraForm),
formsetPrefix = o.prefix, formsetPrefix = o.prefix,
hiddenField = o.hiddenField, hiddenField = o.hiddenField,
removeClass = o.removeClass, removeClass = o.removeClass,
formSelector = o.formSelector, formSelector = o.formSelector,
formPK = o.formPK, formPK = o.formPK,
src = o.src || $delegate.attr('data-src'), src = o.src || $delegate.attr('data-src'),
$input = o.input ? $(o.input) : $delegate.find('input.autocomplete'), $input = o.input ? $(o.input) : $delegate.find('input.autocomplete'),
searchField = o.searchField, searchField = o.searchField,
minLength = o.minSearchLength, minLength = o.minSearchLength,
$maxForms = $('#id_' + formsetPrefix + '-MAX_NUM_FORMS'), $maxForms = $('#id_' + formsetPrefix + '-MAX_NUM_FORMS'),
width = o.width, width = o.width,
addedCB = o.addedCB, addedCB = o.addedCB,
removedCB = o.removedCB, removedCB = o.removedCB,
autocomplete = o.autocomplete, autocomplete = o.autocomplete,
maxItems; maxItems;
if ($maxForms.length && $maxForms.val()) { if ($maxForms.length && $maxForms.val()) {
maxItems = parseInt($maxForms.val(), 10); maxItems = parseInt($maxForms.val(), 10);
} else if (o.maxForms) { } else if (o.maxForms) {
maxItems = o.maxForms; maxItems = o.maxForms;
} }
function findItem(item) { function findItem(item) {
if (item) { if (item) {
var $item = $forms.find('[name$=-' + hiddenField + '][value=' + item[formPK] + ']'); var $item = $forms.find(
if ($item.length) { '[name$=-' + hiddenField + '][value=' + item[formPK] + ']',
var $f = $item.closest(formSelector); );
return {'exists': true, 'visible': $f.is(':visible'), 'item': $f}; if ($item.length) {
} var $f = $item.closest(formSelector);
return { exists: true, visible: $f.is(':visible'), item: $f };
} }
return {'exists': false, 'visible': false}; }
return { exists: false, visible: false };
} }
function clearInput() { function clearInput() {
$input.val(''); $input.val('');
$input.removeAttr('data-item'); $input.removeAttr('data-item');
toggleInput(); toggleInput();
} }
function toggleInput() { function toggleInput() {
if (!maxItems) { if (!maxItems) {
return; return;
} }
var $visible = $forms.find(formSelector + ':visible').length; var $visible = $forms.find(formSelector + ':visible').length;
if ($visible >= maxItems) { if ($visible >= maxItems) {
$input.prop('disabled', true).slideUp(); $input.prop('disabled', true).slideUp();
$('.ui-autocomplete').hide(); $('.ui-autocomplete').hide();
} else if ($visible < maxItems) { } else if ($visible < maxItems) {
$input.filter(':disabled').prop('disabled', false).slideDown(); $input.filter(':disabled').prop('disabled', false).slideDown();
} }
} }
function added() { function added() {
var item = JSON.parse($input.attr('data-item')); var item = JSON.parse($input.attr('data-item'));
// Check if this item has already been added. // Check if this item has already been added.
var dupe = findItem(item); var dupe = findItem(item);
if (dupe.exists) { if (dupe.exists) {
if (!dupe.visible) { if (!dupe.visible) {
// Undelete the item. // Undelete the item.
var $item = dupe.item; var $item = dupe.item;
$item.find('input[name$=-DELETE]').prop('checked', false); $item.find('input[name$=-DELETE]').prop('checked', false);
$item.slideDown(toggleInput); $item.slideDown(toggleInput);
}
clearInput();
return;
} }
clearInput(); clearInput();
return;
}
var formId = updateTotalForms(formsetPrefix, 1) - 1, clearInput();
emptyForm = $extraForm.html().replace(/__prefix__/g, formId);
var $f; var formId = updateTotalForms(formsetPrefix, 1) - 1,
if (addedCB) { emptyForm = $extraForm.html().replace(/__prefix__/g, formId);
$f = addedCB(emptyForm, item);
} else {
$f = $(f);
}
$f.hide().appendTo($forms).slideDown(toggleInput); var $f;
if (addedCB) {
$f = addedCB(emptyForm, item);
} else {
$f = $(f);
}
// Update hidden field. $f.hide().appendTo($forms).slideDown(toggleInput);
$forms.find(formSelector + ':last [name$=-' + hiddenField + ']')
.val(item[formPK]); // Update hidden field.
$forms
.find(formSelector + ':last [name$=-' + hiddenField + ']')
.val(item[formPK]);
} }
function removed(el) { function removed(el) {
el.slideUp(toggleInput); el.slideUp(toggleInput);
// Mark as deleted. // Mark as deleted.
el.find('input[name$=-DELETE]').prop('checked', true); el.find('input[name$=-DELETE]').prop('checked', true);
if (removedCB) { if (removedCB) {
removedCB(el); removedCB(el);
} }
// If this was not an initial form (i.e., an extra form), delete the // If this was not an initial form (i.e., an extra form), delete the
// form and decrement the TOTAL_FORMS count. // form and decrement the TOTAL_FORMS count.
if (!el.find('input[name$=-' + formPK + ']').length) { if (!el.find('input[name$=-' + formPK + ']').length) {
el.remove(); el.remove();
updateTotalForms(formsetPrefix, -1); updateTotalForms(formsetPrefix, -1);
} }
} }
function _renderItem(ul, item) { function _renderItem(ul, item) {
if (!findItem(item).visible) { if (!findItem(item).visible) {
var $a = $(format('<a><img src="{0}" alt="">{1}</a>', var $a = $(
[item.icons['32'], _.escape(item.name)])); format('<a><img src="{0}" alt="">{1}</a>', [
return $('<li>').data('item.autocomplete', item) item.icons['32'],
.append($a).appendTo(ul); _.escape(item.name),
} ]),
);
return $('<li>')
.data('item.autocomplete', item)
.append($a)
.appendTo(ul);
}
} }
function _renderItemData(ul, item) { function _renderItemData(ul, item) {
var rendered = _renderItem( ul, item ); var rendered = _renderItem(ul, item);
// We are overwriting `_renderItem` in some places and return // We are overwriting `_renderItem` in some places and return
// nothing in case of duplicate filtering. // nothing in case of duplicate filtering.
if (rendered) { if (rendered) {
rendered.data("ui-autocomplete-item", item); rendered.data('ui-autocomplete-item', item);
} }
} }
if (autocomplete) { if (autocomplete) {
autocomplete(); autocomplete();
} else { } else {
$input.autocomplete({ $input
minLength: minLength, .autocomplete({
width: width, minLength: minLength,
source: function(request, response) { width: width,
var d = {}; source: function (request, response) {
d[searchField] = request.term; var d = {};
$.getJSON(src, d, response); d[searchField] = request.term;
}, $.getJSON(src, d, response);
focus: function(event, ui) { },
event.preventDefault(); focus: function (event, ui) {
$input.val(ui.item.name); event.preventDefault();
}, $input.val(ui.item.name);
select: function(event, ui) { },
event.preventDefault(); select: function (event, ui) {
if (ui.item) { event.preventDefault();
$input.val(ui.item.name); if (ui.item) {
$input.attr('data-item', JSON.stringify(ui.item)); $input.val(ui.item.name);
added(); $input.attr('data-item', JSON.stringify(ui.item));
} added();
} }
}).data('ui-autocomplete')._renderMenu = function(ul, items) { },
// Overwrite _renderMenu to patch in our custom `_renderItemData` })
// and `_renderItem` to allow for our custom list-filter. .data('ui-autocomplete')._renderMenu = function (ul, items) {
$.each(items, function(index, item) { // Overwrite _renderMenu to patch in our custom `_renderItemData`
_renderItemData(ul, item); // and `_renderItem` to allow for our custom list-filter.
}); $.each(items, function (index, item) {
}; _renderItemData(ul, item);
});
};
} }
toggleInput(); toggleInput();
$delegate.on('click', '.' + removeClass, _pd(function() { $delegate.on(
'click',
'.' + removeClass,
_pd(function () {
removed($(this).closest(formSelector)); removed($(this).closest(formSelector));
})); }),
);
}; };
})(jQuery); })(jQuery);

Просмотреть файл

@ -1,151 +1,175 @@
//bind pager controls to our addon grids //bind pager controls to our addon grids
$('.island .listing-grid').on('grid.init', function(e, data) { $('.island .listing-grid').on('grid.init', function (e, data) {
var $grid = data.self, var $grid = data.self,
numPages = data.maxPage; numPages = data.maxPage;
if (numPages > 0) { if (numPages > 0) {
var $nav = $('<nav class="pager">'); var $nav = $('<nav class="pager">');
$nav.append('<a href="#" class="prev">&laquo;</a>'); $nav.append('<a href="#" class="prev">&laquo;</a>');
for (var i=0; i<=numPages; i++) { for (var i = 0; i <= numPages; i++) {
$nav.append('<a href="#" class="' + (i==0 ? 'selected ': '') + 'dot"><b></b></a>'); $nav.append(
} '<a href="#" class="' +
$nav.append('<a href="#" class="next">&raquo;</a>'); (i == 0 ? 'selected ' : '') +
$grid.parent().prepend($nav); 'dot"><b></b></a>',
$nav.on('click', 'a', function(e) { );
e.preventDefault();
var $tgt = $(this);
if ($tgt.hasClass('dot')) {
$grid.go($tgt.index() - 1);
} else if ($tgt.hasClass('prev')){
$grid.prevPage();
} else if ($tgt.hasClass('next')){
$grid.nextPage();
}
});
$grid.on('grid.update', function(e, data) {
$nav.find('.dot').removeClass('selected')
.eq(data.current).addClass('selected');
});
} }
$nav.append('<a href="#" class="next">&raquo;</a>');
$grid.parent().prepend($nav);
$nav.on('click', 'a', function (e) {
e.preventDefault();
var $tgt = $(this);
if ($tgt.hasClass('dot')) {
$grid.go($tgt.index() - 1);
} else if ($tgt.hasClass('prev')) {
$grid.prevPage();
} else if ($tgt.hasClass('next')) {
$grid.nextPage();
}
});
$grid.on('grid.update', function (e, data) {
$nav
.find('.dot')
.removeClass('selected')
.eq(data.current)
.addClass('selected');
});
}
}); });
function hoverTruncate(grid) { function hoverTruncate(grid) {
var $grid = $(grid); var $grid = $(grid);
if ($grid.hasClass('hovercard')) { if ($grid.hasClass('hovercard')) {
$grid = $grid.parent(); $grid = $grid.parent();
} }
$grid.find('.hovercard h3').truncate(); $grid.find('.hovercard h3').truncate();
$grid.on('mouseover', '.hovercard', function() { $grid
var $el = $(this); .on('mouseover', '.hovercard', function () {
setTimeout(function() { var $el = $(this);
$el.find('h3').untruncate(); setTimeout(function () {
}, 100); $el.find('h3').untruncate();
}).on('mouseout', '.hovercard', function() { }, 100);
var $el = $(this); })
setTimeout(function() { .on('mouseout', '.hovercard', function () {
$el.find('h3').truncate(); var $el = $(this);
}, 100); setTimeout(function () {
$el.find('h3').truncate();
}, 100);
}); });
} }
function listing_grid() { function listing_grid() {
var $grid = $(this), var $grid = $(this),
$pages = $grid.find('section'), $pages = $grid.find('section'),
current = 0, current = 0,
maxPage = $pages.length-1; maxPage = $pages.length - 1;
$grid.trigger("grid.init", {self: $grid, current: current, maxPage: maxPage}); $grid.trigger('grid.init', {
$grid.go = function(n) { self: $grid,
if (n != current) { current: current,
n = n < 0 ? 0 : (n > maxPage ? maxPage : n); maxPage: maxPage,
current = n; });
$pages.hide().eq(n).show().find('.hovercard h3').truncate(); $grid.go = function (n) {
$grid.trigger("grid.update", {self: $grid, current: current, maxPage: maxPage}); if (n != current) {
} n = n < 0 ? 0 : n > maxPage ? maxPage : n;
}; current = n;
$grid.prevPage = function() { $pages.hide().eq(n).show().find('.hovercard h3').truncate();
$grid.go(current-1); $grid.trigger('grid.update', {
}; self: $grid,
$grid.nextPage = function() { current: current,
$grid.go(current+1); maxPage: maxPage,
}; });
hoverTruncate(this); }
$grid.css({ };
'width': $grid.width() + 'px', $grid.prevPage = function () {
'height': $grid.height() + 'px' $grid.go(current - 1);
}); };
return $grid; $grid.nextPage = function () {
$grid.go(current + 1);
};
hoverTruncate(this);
$grid.css({
width: $grid.width() + 'px',
height: $grid.height() + 'px',
});
return $grid;
} }
$(function () {
'use strict';
$(function() { // Paginate listing grids.
"use strict"; $('.listing-grid').each(listing_grid);
// Paginate listing grids. // Truncate titles on single hovercards.
$('.listing-grid').each(listing_grid); $('.hovercard').each(function () {
hoverTruncate(this);
});
// Truncate titles on single hovercards. // load deferred images.
$('.hovercard').each(function() { $('img[data-defer-src]').each(function () {
hoverTruncate(this); var $img = $(this);
}); $img.attr('src', $img.attr('data-defer-src'));
});
// load deferred images. // Email obfuscation.
$('img[data-defer-src]').each(function() { $('span.emaillink').each(function () {
var $img = $(this); var $this = $(this);
$img.attr('src', $img.attr('data-defer-src')); $this.find('.i').remove();
}); var em = $this.text().split('').reverse().join('');
$this.prev('a').attr('href', 'mailto:' + em);
// Allows the email to be selected and pasted in webmails, see #919160.
$this.text(em).css('direction', 'ltr');
});
// Email obfuscation. $('#page').on(
$('span.emaillink').each(function() { 'click',
var $this = $(this); '.expando .toggle',
$this.find('.i').remove(); _pd(function () {
var em = $this.text().split('').reverse().join(''); $(this).closest('.expando').toggleClass('expanded');
$this.prev('a').attr('href', 'mailto:' + em); }),
// Allows the email to be selected and pasted in webmails, see #919160. );
$this.text(em).css('direction', 'ltr');
});
$('#page').on('click', '.expando .toggle', _pd(function() { var fragment = window.location.hash;
$(this).closest('.expando').toggleClass('expanded'); if (fragment && /^#[\w.-]+$/.test(fragment)) {
})); // If the page URL is pointing directly to an expando section (e.g.
// external link to that section), make sure the contents are visible.
var $target = $(fragment);
if ($target.hasClass('expando')) $target.addClass('expanded');
}
var fragment = window.location.hash; $('#page').on('click', '.scrollto', function (e) {
if (fragment && /^#[\w.-]+$/.test(fragment)) { e.preventDefault();
// If the page URL is pointing directly to an expando section (e.g. var href = $(this).attr('href'),
// external link to that section), make sure the contents are visible. $target = $(href.match(/#.*$/)[0]);
var $target = $(fragment); if ($target.hasClass('expando')) {
if ($target.hasClass('expando')) $target.addClass('expanded');
$target.addClass('expanded');
} }
var top = $target.offset().top - 15;
$(document.documentElement).animate({ scrollTop: top }, 500);
});
$('#page').on('click', '.scrollto', function(e) { $("select[name='rating']").ratingwidget();
e.preventDefault();
var href = $(this).attr('href'),
$target = $(href.match(/#.*$/)[0]);
if ($target.hasClass('expando')) {
$target.addClass('expanded');
}
var top = $target.offset().top - 15;
$(document.documentElement).animate({ scrollTop: top }, 500);
});
$("select[name='rating']").ratingwidget();
}); });
// AJAX form submit // AJAX form submit
$(function() { $(function () {
$(document).on('submit', 'form.ajax-submit, .ajax-submit form', function() { $(document).on('submit', 'form.ajax-submit, .ajax-submit form', function () {
var $form = $(this), var $form = $(this),
$parent = $form.is('.ajax-submit') ? $form : $form.closest('.ajax-submit'), $parent = $form.is('.ajax-submit')
params = $form.serializeArray(); ? $form
: $form.closest('.ajax-submit'),
params = $form.serializeArray();
$form.find('.submit, button[type=submit], submit').prop('disabled', true).addClass('loading-submit'); $form
$.post($form.attr('action'), params, function(d) { .find('.submit, button[type=submit], submit')
var $replacement = $(d); .prop('disabled', true)
$parent.replaceWith($replacement); .addClass('loading-submit');
$replacement.trigger('ajax-submit-loaded'); $.post($form.attr('action'), params, function (d) {
$replacement.find('.ajax-submit').trigger('ajax-submit-loaded'); var $replacement = $(d);
}); $parent.replaceWith($replacement);
return false; $replacement.trigger('ajax-submit-loaded');
$replacement.find('.ajax-submit').trigger('ajax-submit-loaded');
});
return false;
}); });
}); });

Просмотреть файл

@ -1,44 +1,48 @@
$(function() { $(function () {
initListingCompat(); initListingCompat();
$('.theme-grid .hovercard.theme').each(function() { $('.theme-grid .hovercard.theme').each(function () {
var $this = $(this); var $this = $(this);
if ($this.find('.acr-override').length) { if ($this.find('.acr-override').length) {
$this.addClass('acr'); $this.addClass('acr');
} else if ($this.find('.concealed').length == $this.find('.button').length) { } else if (
$this.addClass('incompatible'); $this.find('.concealed').length == $this.find('.button').length
// L10n: {0} is an app name. ) {
var msg = format(gettext('This theme is incompatible with your version of {0}'), $this.addClass('incompatible');
[z.appName]); // L10n: {0} is an app name.
$this.append(format('<span class="notavail">{0}</span>', msg)); var msg = format(
} gettext('This theme is incompatible with your version of {0}'),
}); [z.appName],
);
$this.append(format('<span class="notavail">{0}</span>', msg));
}
});
// Make this row appear 'static' so the installation buttons and pop-ups
// Make this row appear 'static' so the installation buttons and pop-ups // stay open when hovering outside the item row.
// stay open when hovering outside the item row. $(document.body)
$(document.body).on('newStatic', function() { .on('newStatic', function () {
$('.install-note:visible').closest('.item').addClass('static'); $('.install-note:visible').closest('.item').addClass('static');
}).on('closeStatic', function() { })
$('.item.static').removeClass('static'); .on('closeStatic', function () {
$('.item.static').removeClass('static');
}); });
}); });
function initListingCompat(domContext) { function initListingCompat(domContext) {
domContext = domContext || document.body; domContext = domContext || document.body;
// Mark incompatible add-ons on listing pages unless marked with ignore. // Mark incompatible add-ons on listing pages unless marked with ignore.
$('.listing .item.addon', domContext).each(function() { $('.listing .item.addon', domContext).each(function () {
var $this = $(this); var $this = $(this);
var isIncompatible = (!$this.hasClass('ignore-compatibility') && var isIncompatible =
($this.find('.concealed').length == $this.find('.button').length || !$this.hasClass('ignore-compatibility') &&
$this.find('button.not-available').length) ($this.find('.concealed').length == $this.find('.button').length ||
); $this.find('button.not-available').length);
if ($this.find('.acr-override').length) { if ($this.find('.acr-override').length) {
$this.addClass('acr'); $this.addClass('acr');
} else if (isIncompatible) { } else if (isIncompatible) {
$this.addClass('incompatible'); $this.addClass('incompatible');
} }
}); });
} }

Просмотреть файл

@ -11,23 +11,23 @@
* *
*/ */
(function() { (function () {
var $window = $(window); var $window = $(window);
$window.on('click', '.install-button a.button', function(e) { $window.on('click', '.install-button a.button', function (e) {
e.preventDefault(); e.preventDefault();
var $el = $(this); var $el = $(this);
// When everything is loaded, trigger a click on the button // When everything is loaded, trigger a click on the button
$window.on('buttons_loaded_click', function() { $window.on('buttons_loaded_click', function () {
$el.trigger('click'); $el.trigger('click');
});
}); });
$window.on('buttons_loaded', function() { });
// Trigger all the clicks $window.on('buttons_loaded', function () {
$window.trigger('buttons_loaded_click'); // Trigger all the clicks
$window.trigger('buttons_loaded_click');
// Clean up after ourselves // Clean up after ourselves
$window.off('buttons_loaded buttons_loaded_click'); $window.off('buttons_loaded buttons_loaded_click');
$window.off('click', '.install-button a.button'); $window.off('click', '.install-button a.button');
}); });
})(); })();

Просмотреть файл

@ -1,163 +1,185 @@
$(document).ready(function() { $(document).ready(function () {
var report = $('.review-reason').html(),
$window = $(window);
var report = $('.review-reason').html(), $('.review-reason').popup('.flag-review', {
$window = $(window); delegate: $(document.body),
width: 'inherit',
callback: function (obj) {
var ct = $(obj.click_target),
$popup = this;
//reset our event handlers
$popup.hideMe();
$('.review-reason').popup('.flag-review', { function addFlag(flag, note) {
delegate: $(document.body), $.ajax({
width: 'inherit', type: 'POST',
callback: function(obj) { url: ct.attr('href'),
var ct = $(obj.click_target), data: { flag: flag, note: note },
$popup = this; success: function () {
//reset our event handlers $popup.removeClass('other').hideMe();
$popup.hideMe(); ct.closest('.item').addClass('flagged');
ct.replaceWith(gettext('Flagged for review')).addClass('flagged');
function addFlag(flag, note) { },
$.ajax({type: 'POST', error: function () {},
url: ct.attr('href'), dataType: 'json',
data: {flag: flag, note: note},
success: function() {
$popup.removeClass('other')
.hideMe();
ct.closest('.item').addClass('flagged');
ct.replaceWith(gettext('Flagged for review'))
.addClass('flagged');
},
error: function(){ },
dataType: 'json'
});
};
$popup.on('click', 'li a', function(e) {
e.preventDefault();
var el = $(e.target);
if (el.attr('href') == '#review_flag_reason_other') {
$popup.addClass('other')
.on('submit', 'form', function(e) {
e.preventDefault();
var note = $popup.find('#id_note').val();
if (!note) {
alert(gettext('Your input is required'));
} else {
addFlag('review_flag_reason_other', note);
}
})
.setPos(ct)
.find('input[type=text]')
.focus();
} else {
addFlag(el.attr('href').slice(1));
}
});
$popup.removeClass("other");
$popup.html(report);
return { pointTo: ct };
}
});
// A review comment can either be a review or a review reply
function review_comment_edit_click(comment_form_id, comment_title_widget_id, comment_body_widget_id, comment_cancel_btn_id) {
return function(e) {
e.preventDefault();
var $form = $('#' + comment_form_id),
$review = $(this).closest('.review'),
edit_url = $('a.permalink', $review).attr('href') + 'edit',
$cancel = $('#' + comment_cancel_btn_id),
title_selector;
clearErrors($form);
$form.off().hide();
$('.review').not($review).show();
$form.detach().insertAfter($review);
if ($review.find('h4').length) {
$form.find('fieldset h3').remove();
title_selector = 'h4 > b';
$form.find('fieldset').prepend($review.find('h3').clone());
} else {
title_selector = 'h3 > b';
}
$form.find('#' + comment_title_widget_id).val($review.find(title_selector).text());
$form.find('#' + comment_body_widget_id).val($review.children('p.description').html().replace(/<br>/g, '\n'));
$review.hide();
$form.show();
$window.resize();
location.hash = '#' + comment_form_id;
function done_edit() {
clearErrors($form);
$form.off().hide();
$review.show();
$cancel.off();
$window.resize();
}
$cancel.click(_pd(done_edit));
$form.submit(function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: edit_url,
data: $form.serialize(),
success: function(response, status) {
clearErrors($form);
$review.find(title_selector).text($form.find('#' + comment_title_widget_id).val());
var rating = $form.find('.ratingwidget input:radio:checked').val();
$('.stars', $review).removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5').addClass('stars-' + rating);
rating = $review.attr('data-rating', rating);
$review.children('p.description').html(
$form.find('#' + comment_body_widget_id).val()
.replace(/&/g,'&amp;')
.replace(/</g,'&lt;')
.replace(/>/g,'&gt;')
.replace(/\n/g, '<br>'));
done_edit();
},
error: function(xhr) {
var errors = JSON.parse(xhr.responseText);
populateErrors($form, errors);
},
dataType: 'json'
});
return false;
});
}
}
$('.primary').on('click', '.review-reply-edit',
review_comment_edit_click(
'review-reply-edit-form',
'id_review_reply_title',
'id_review_reply_body',
'review-reply-edit-cancel'
)
);
$('.primary').on('click', '.review-edit',
review_comment_edit_click(
'review-edit-form',
'id_review_title',
'id_review_body',
'review-edit-cancel'
)
);
$('.delete-review').click(function(e) {
e.preventDefault();
var target = $(e.target);
$.post(target.attr('href'), function() {
target.replaceWith(gettext('Marked for deletion'));
}); });
target.closest('.review').addClass('deleted'); }
$popup.on('click', 'li a', function (e) {
e.preventDefault();
var el = $(e.target);
if (el.attr('href') == '#review_flag_reason_other') {
$popup
.addClass('other')
.on('submit', 'form', function (e) {
e.preventDefault();
var note = $popup.find('#id_note').val();
if (!note) {
alert(gettext('Your input is required'));
} else {
addFlag('review_flag_reason_other', note);
}
})
.setPos(ct)
.find('input[type=text]')
.focus();
} else {
addFlag(el.attr('href').slice(1));
}
});
$popup.removeClass('other');
$popup.html(report);
return { pointTo: ct };
},
});
// A review comment can either be a review or a review reply
function review_comment_edit_click(
comment_form_id,
comment_title_widget_id,
comment_body_widget_id,
comment_cancel_btn_id,
) {
return function (e) {
e.preventDefault();
var $form = $('#' + comment_form_id),
$review = $(this).closest('.review'),
edit_url = $('a.permalink', $review).attr('href') + 'edit',
$cancel = $('#' + comment_cancel_btn_id),
title_selector;
clearErrors($form);
$form.off().hide();
$('.review').not($review).show();
$form.detach().insertAfter($review);
if ($review.find('h4').length) {
$form.find('fieldset h3').remove();
title_selector = 'h4 > b';
$form.find('fieldset').prepend($review.find('h3').clone());
} else {
title_selector = 'h3 > b';
}
$form
.find('#' + comment_title_widget_id)
.val($review.find(title_selector).text());
$form
.find('#' + comment_body_widget_id)
.val($review.children('p.description').html().replace(/<br>/g, '\n'));
$review.hide();
$form.show();
$window.resize();
location.hash = '#' + comment_form_id;
function done_edit() {
clearErrors($form);
$form.off().hide();
$review.show();
$cancel.off();
$window.resize();
}
$cancel.click(_pd(done_edit));
$form.submit(function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: edit_url,
data: $form.serialize(),
success: function (response, status) {
clearErrors($form);
$review
.find(title_selector)
.text($form.find('#' + comment_title_widget_id).val());
var rating = $form.find('.ratingwidget input:radio:checked').val();
$('.stars', $review)
.removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5')
.addClass('stars-' + rating);
rating = $review.attr('data-rating', rating);
$review.children('p.description').html(
$form
.find('#' + comment_body_widget_id)
.val()
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '<br>'),
);
done_edit();
},
error: function (xhr) {
var errors = JSON.parse(xhr.responseText);
populateErrors($form, errors);
},
dataType: 'json',
});
return false;
});
};
}
$('.primary').on(
'click',
'.review-reply-edit',
review_comment_edit_click(
'review-reply-edit-form',
'id_review_reply_title',
'id_review_reply_body',
'review-reply-edit-cancel',
),
);
$('.primary').on(
'click',
'.review-edit',
review_comment_edit_click(
'review-edit-form',
'id_review_title',
'id_review_body',
'review-edit-cancel',
),
);
$('.delete-review').click(function (e) {
e.preventDefault();
var target = $(e.target);
$.post(target.attr('href'), function () {
target.replaceWith(gettext('Marked for deletion'));
}); });
target.closest('.review').addClass('deleted');
});
$('select[name="rating"]').ratingwidget(); $('select[name="rating"]').ratingwidget();
$('#detail-review-link').click(_pd(function(e) { $('#detail-review-link').click(
$('#review-add-box form') _pd(function (e) {
.append('<input type="hidden" name="detailed" value="1">').submit(); $('#review-add-box form')
})); .append('<input type="hidden" name="detailed" value="1">')
.submit();
}),
);
}); });

Просмотреть файл

@ -1,178 +1,184 @@
(function () { (function () {
var appver_input = $('#id_appver'); var appver_input = $('#id_appver');
var platform_input = $('#id_platform'); var platform_input = $('#id_platform');
function autofillPlatform(context) { function autofillPlatform(context) {
var $context = $(context || document.body); var $context = $(context || document.body);
$('#search', $context).on('autofill', function(e) { $('#search', $context)
var $this = $(this); .on('autofill', function (e) {
var $this = $(this);
// Bail if search is present but not the appver input somehow. // Bail if search is present but not the appver input somehow.
if (!appver_input.length) { if (!appver_input.length) {
return; return;
} }
// Populate search form with browser version and OS. // Populate search form with browser version and OS.
var gv = z.getVars(); var gv = z.getVars();
// Facets are either the ones defined in the URL, or the detected // Facets are either the ones defined in the URL, or the detected
// browser version and platform. // browser version and platform.
if (!!(gv.appver)) { // Defined in URL parameter if (!!gv.appver) {
appver_input.val(gv.appver); // Defined in URL parameter
} else if (z.appMatchesUserAgent) { // Fallback to detected appver_input.val(gv.appver);
// Only do this if firefox 57 or higher. Lower versions default } else if (z.appMatchesUserAgent) {
// to searching for all add-ons even if they might be // Fallback to detected
// incompatible. https://github.com/mozilla/addons-server/issues/5482 // Only do this if firefox 57 or higher. Lower versions default
if (VersionCompare.compareVersions(z.browserVersion, '57.0') >= 0) { // to searching for all add-ons even if they might be
appver_input.val(z.browserVersion); // incompatible. https://github.com/mozilla/addons-server/issues/5482
} if (VersionCompare.compareVersions(z.browserVersion, '57.0') >= 0) {
} appver_input.val(z.browserVersion);
}
}
if (!!(gv.platform)) { // Defined in URL parameter if (!!gv.platform) {
platform_input.val(gv.platform); // Defined in URL parameter
} else if (z.appMatchesUserAgent) { // Fallback to detected platform_input.val(gv.platform);
platform_input.val(z.platform); } else if (z.appMatchesUserAgent) {
} // Fallback to detected
}).trigger('autofill'); platform_input.val(z.platform);
}
})
.trigger('autofill');
}
autofillPlatform();
$(function () {
$('#search-facets')
.on('click', 'li.facet', function (e) {
var $this = $(this);
if ($this.hasClass('active')) {
if ($(e.target).is('a')) {
return;
}
$this.removeClass('active');
} else {
$this.closest('ul').find('.active').removeClass('active');
$this.addClass('active');
}
})
.on('highlight', 'a', function (e) {
// Highlight selection on sidebar.
var $this = $(this);
$this.closest('.facet-group').find('.selected').removeClass('selected');
$this.closest('li').addClass('selected');
})
.on('recount', '.cnt', function (e, newCount) {
// Update # of results on sidebar.
var $this = $(this);
if (newCount.length && $this.html() != newCount.html()) {
$this.replaceWith(newCount);
}
})
.on('rebuild', 'a[data-params]', function (e) {
var $this = $(this),
url = rebuildLink($this.attr('href'), $this.attr('data-params'));
$this.attr('href', url);
});
if ($('body').hasClass('pjax') && $.support.pjax && z.capabilities.JSON) {
$('#pjax-results').initSearchPjax($('#search-facets'), '#pjax-results');
} }
});
function rebuildLink(url, urlparams, qs) {
var params = JSON.parseNonNull(urlparams),
newVars = $.extend(z.getVars(qs, true), params);
return url.split('?')[0] + '?' + $.param(newVars);
}
autofillPlatform(); $.fn.initSearchPjax = function ($filters, containerSelector) {
var $container = $(this),
container = containerSelector,
$triggered;
function pjaxOpen(url) {
$(function() { var urlBase = location.pathname + location.search;
$('#search-facets').on('click', 'li.facet', function(e) { if (!!url && url != '#' && url != urlBase) {
var $this = $(this); $.pjax({
if ($this.hasClass('active')) { url: url,
if ($(e.target).is('a')) { container: container,
return; timeout: 5000,
}
$this.removeClass('active');
} else {
$this.closest('ul').find('.active').removeClass('active');
$this.addClass('active');
}
}).on('highlight', 'a', function(e) {
// Highlight selection on sidebar.
var $this = $(this);
$this.closest('.facet-group').find('.selected').removeClass('selected');
$this.closest('li').addClass('selected');
}).on('recount', '.cnt', function(e, newCount) {
// Update # of results on sidebar.
var $this = $(this);
if (newCount.length && $this.html() != newCount.html()) {
$this.replaceWith(newCount);
}
}).on('rebuild', 'a[data-params]', function(e) {
var $this = $(this),
url = rebuildLink($this.attr('href'), $this.attr('data-params'));
$this.attr('href', url);
}); });
if ($('body').hasClass('pjax') && $.support.pjax && z.capabilities.JSON) { }
$('#pjax-results').initSearchPjax($('#search-facets'), '#pjax-results');
}
});
function rebuildLink(url, urlparams, qs) {
var params = JSON.parseNonNull(urlparams),
newVars = $.extend(z.getVars(qs, true), params);
return url.split('?')[0] + '?' + $.param(newVars);
} }
function hijackLink() {
$triggered = $(this);
pjaxOpen($triggered.attr('href'));
}
$.fn.initSearchPjax = function($filters, containerSelector) { function loading() {
var $container = $(this), var $wrapper = $container.closest('.results'),
container = containerSelector, msg = gettext('Updating results&hellip;'),
$triggered; cls = 'updating';
$wrapper.addClass('loading');
function pjaxOpen(url) { // The loading throbber is absolutely positioned atop the
var urlBase = location.pathname + location.search; // search results, so we do this to ensure a max-margin of sorts.
if (!!url && url != '#' && url != urlBase) { if ($container.outerHeight() > 300) {
$.pjax({ cls += ' tall';
url: url, }
container: container,
timeout: 5000 // Insert the loading throbber.
}); $('<div>', { class: cls, html: msg }).insertBefore($container);
}
$container.trigger('search.loading');
}
function finished() {
var $wrapper = $container.closest('.results');
// Initialize install buttons and compatibility checking.
$.when($container.find('.install:not(.triggered)').installButton()).done(
function () {
$container.find('.install').addClass('triggered');
initListingCompat();
},
);
// Remove the loading throbber.
$wrapper.removeClass('loading').find('.updating').remove();
// Update the # of matching results on sidebar.
$filters.find('.cnt').trigger('recount', [$wrapper.find('.cnt')]);
// Update GET parameters of sidebar anchors.
$filters.find('a[data-params]').trigger('rebuild');
// Highlight selection on sidebar.
if ($triggered) {
$triggered.trigger('highlight');
}
// Update auto-filled appver/platform if there's a user override.
$('#search').trigger('autofill');
// Scroll up to top of page.
$('html, body').animate({ scrollTop: 0 }, 200);
$container.trigger('search.finished');
}
function turnPages(e) {
if (fieldFocused(e)) {
return;
}
if (e.which == $.ui.keyCode.LEFT || e.which == $.ui.keyCode.RIGHT) {
e.preventDefault();
var sel;
if (e.which == $.ui.keyCode.LEFT) {
sel = '.paginator .prev:not(.disabled)';
} else {
sel = '.paginator .next:not(.disabled)';
} }
pjaxOpen($container.find(sel).attr('href'));
}
}
function hijackLink() { $(document).on('click', '.pjax-trigger a', _pd(hijackLink));
$triggered = $(this); $container.on('pjax:start', loading).on('pjax:end', finished);
pjaxOpen($triggered.attr('href')); $(document).keyup(_.throttle(turnPages, 300));
} };
function loading() {
var $wrapper = $container.closest('.results'),
msg = gettext('Updating results&hellip;'),
cls = 'updating';
$wrapper.addClass('loading');
// The loading throbber is absolutely positioned atop the
// search results, so we do this to ensure a max-margin of sorts.
if ($container.outerHeight() > 300) {
cls += ' tall';
}
// Insert the loading throbber.
$('<div>', {'class': cls, 'html': msg}).insertBefore($container);
$container.trigger('search.loading');
}
function finished() {
var $wrapper = $container.closest('.results');
// Initialize install buttons and compatibility checking.
$.when($container.find('.install:not(.triggered)')
.installButton()).done(function() {
$container.find('.install').addClass('triggered');
initListingCompat();
});
// Remove the loading throbber.
$wrapper.removeClass('loading').find('.updating').remove();
// Update the # of matching results on sidebar.
$filters.find('.cnt').trigger('recount', [$wrapper.find('.cnt')]);
// Update GET parameters of sidebar anchors.
$filters.find('a[data-params]').trigger('rebuild');
// Highlight selection on sidebar.
if ($triggered) {
$triggered.trigger('highlight');
}
// Update auto-filled appver/platform if there's a user override.
$('#search').trigger('autofill');
// Scroll up to top of page.
$('html, body').animate({scrollTop: 0}, 200);
$container.trigger('search.finished');
}
function turnPages(e) {
if (fieldFocused(e)) {
return;
}
if (e.which == $.ui.keyCode.LEFT || e.which == $.ui.keyCode.RIGHT) {
e.preventDefault();
var sel;
if (e.which == $.ui.keyCode.LEFT) {
sel = '.paginator .prev:not(.disabled)';
} else {
sel = '.paginator .next:not(.disabled)';
}
pjaxOpen($container.find(sel).attr('href'));
}
}
$(document).on('click', '.pjax-trigger a', _pd(hijackLink));
$container.on('pjax:start', loading).on('pjax:end', finished);
$(document).keyup(_.throttle(turnPages, 300));
};
})(); })();

Просмотреть файл

@ -1,26 +1,29 @@
z.getVars = function(qs, excl_undefined) { z.getVars = function (qs, excl_undefined) {
if (typeof qs === 'undefined') { if (typeof qs === 'undefined') {
qs = location.search; qs = location.search;
} }
if (qs && qs[0] == '?') { if (qs && qs[0] == '?') {
qs = qs.substr(1); // Filter off the leading ? if it's there. qs = qs.substr(1); // Filter off the leading ? if it's there.
} }
if (!qs) return {}; if (!qs) return {};
return _.chain(qs.split('&')) // ['a=b', 'c=d'] return _.chain(qs.split('&')) // ['a=b', 'c=d']
.map(function(c) {return _.map(c.split('='), escape_);}) // [['a', 'b'], ['c', 'd']] .map(function (c) {
.filter(function(p) { // [['a', 'b'], ['c', undefined]] -> [['a', 'b']] return _.map(c.split('='), escape_);
return !!p[0] && (!excl_undefined || !_.isUndefined(p[1])); }) // [['a', 'b'], ['c', 'd']]
}).object() // {'a': 'b', 'c': 'd'} .filter(function (p) {
.value(); // [['a', 'b'], ['c', undefined]] -> [['a', 'b']]
return !!p[0] && (!excl_undefined || !_.isUndefined(p[1]));
})
.object() // {'a': 'b', 'c': 'd'}
.value();
}; };
JSON.parseNonNull = function (text) {
JSON.parseNonNull = function(text) { return JSON.parse(text, function (key, value) {
return JSON.parse(text, function(key, value) { if (typeof value === 'object' && value === null) {
if (typeof value === 'object' && value === null) { return '';
return ''; }
} return value;
return value; });
});
}; };

Просмотреть файл

@ -1,63 +1,65 @@
// Init site search suggestions and populate the suggestions container. // Init site search suggestions and populate the suggestions container.
(function() { (function () {
// AMO search init. // AMO search init.
$('#search #search-q').searchSuggestions($('#site-search-suggestions'), $('#search #search-q').searchSuggestions(
processResults, 'AMO'); $('#site-search-suggestions'),
processResults,
'AMO',
);
function processResults(settings) { function processResults(settings) {
if (!settings || !settings.category) { if (!settings || !settings.category) {
return; return;
}
// Update the 'Search add-ons for <b>"{addon}"</b>' text.
settings['$results'].find('p b').html(format('"{0}"',
settings.searchTerm));
var li_item = template(
'<li><a href="{url}"><span {cls} {icon}>{name}</span>{subtitle}</a></li>'
);
$.ajaxCache({
url: settings['$results'].attr('data-src'),
data: settings['$form'].serialize() + '&cat=' + settings.category,
newItems: function(formdata, items) {
var eventName;
if (items !== undefined) {
var ul = '';
$.each(items, function(i, item) {
var d = {
url: escape_(item.url) || '#',
icon: '',
cls: '',
subtitle: ''
};
if (item.icons && item.icons['32']) {
d.icon = format(
'style="background-image:url({0})"',
escape_(item.icons['32'])
);
}
if (item.cls) {
d.cls = format('class="{0}"',
escape_(item.cls));
if (item.cls == 'cat') {
d.subtitle = format(
' <em class="subtitle">{0}</em>',
gettext('Category')
);
}
}
if (item.name) {
d.name = escape_(item.name);
// Append the item only if it has a name.
ul += li_item(d);
}
});
settings['$results'].find('ul').html(ul);
}
settings['$results'].trigger('highlight', [settings.searchTerm])
.trigger('resultsUpdated', [items]);
}
});
} }
// Update the 'Search add-ons for <b>"{addon}"</b>' text.
settings['$results'].find('p b').html(format('"{0}"', settings.searchTerm));
var li_item = template(
'<li><a href="{url}"><span {cls} {icon}>{name}</span>{subtitle}</a></li>',
);
$.ajaxCache({
url: settings['$results'].attr('data-src'),
data: settings['$form'].serialize() + '&cat=' + settings.category,
newItems: function (formdata, items) {
var eventName;
if (items !== undefined) {
var ul = '';
$.each(items, function (i, item) {
var d = {
url: escape_(item.url) || '#',
icon: '',
cls: '',
subtitle: '',
};
if (item.icons && item.icons['32']) {
d.icon = format(
'style="background-image:url({0})"',
escape_(item.icons['32']),
);
}
if (item.cls) {
d.cls = format('class="{0}"', escape_(item.cls));
if (item.cls == 'cat') {
d.subtitle = format(
' <em class="subtitle">{0}</em>',
gettext('Category'),
);
}
}
if (item.name) {
d.name = escape_(item.name);
// Append the item only if it has a name.
ul += li_item(d);
}
});
settings['$results'].find('ul').html(ul);
}
settings['$results']
.trigger('highlight', [settings.searchTerm])
.trigger('resultsUpdated', [items]);
},
});
}
})(); })();

Разница между файлами не показана из-за своего большого размера Загрузить разницу

199
static/js/impala/stats/controls.js поставляемый
Просмотреть файл

@ -1,105 +1,106 @@
(function (){ (function () {
"use strict"; 'use strict';
var $rangeSelector = $(".criteria.range ul"), var $rangeSelector = $('.criteria.range ul'),
$customRangeForm = $("div.custom.criteria"), $customRangeForm = $('div.custom.criteria'),
$groupSelector = $(".criteria.group ul"), $groupSelector = $('.criteria.group ul'),
minDate = Date.iso($('.primary').attr('data-min-date')), minDate = Date.iso($('.primary').attr('data-min-date')),
msDay = 24 * 60 * 60 * 1000; // One day in milliseconds. msDay = 24 * 60 * 60 * 1000; // One day in milliseconds.
$.datepicker.setDefaults({showAnim: ''}); $.datepicker.setDefaults({ showAnim: '' });
var $customModal = $("#custom-criteria").modal("#custom-date-range", var $customModal = $('#custom-criteria').modal('#custom-date-range', {
{ width: 520, width: 520,
hideme: true}); hideme: true,
var $startPicker = $("#start-date-picker").datepicker({ });
maxDate: 0, var $startPicker = $('#start-date-picker').datepicker({
minDate: minDate, maxDate: 0,
dateFormat: 'yy-mm-dd', minDate: minDate,
onSelect: function(dateText) { dateFormat: 'yy-mm-dd',
$("#date-range-start").val(dateText); onSelect: function (dateText) {
$('#date-range-start').val(dateText);
},
});
var $endPicker = $('#end-date-picker').datepicker({
maxDate: 0,
minDate: minDate,
dateFormat: 'yy-mm-dd',
onSelect: function (dateText) {
$('#date-range-end').val(dateText);
},
});
$rangeSelector.click(function (e) {
var $target = $(e.target).parent();
var newRange = $target.attr('data-range');
if (newRange && newRange != 'custom') {
$target.trigger('changeview', { range: newRange });
}
e.preventDefault();
});
$groupSelector.on('click', 'a', function (e) {
var $target = $(this).parent(),
newGroup = $target.attr('data-group');
$(this).trigger('changeview', { group: newGroup });
e.preventDefault();
});
// set controls when `changeview` is detected.
$(window).on('changeview', function (e, newState) {
if (!newState) return;
function populateCustomRange() {
var nRange = normalizeRange(newState.range),
startStr = nRange.start.iso(),
endStr = nRange.end.iso();
// Trim nRange.end by one day if custom range.
if (newState.range.custom) {
nRange.end = new Date(nRange.end.getTime() - msDay);
endStr = nRange.end.iso();
}
$('#date-range-start').val(startStr);
$startPicker.datepicker('setDate', startStr);
$('#date-range-end').val(endStr);
$endPicker.datepicker('setDate', endStr);
}
if (newState.range) {
if (!newState.range.custom) {
var newRange = newState.range,
$rangeEl = $('li[data-range="' + _.escape(newRange) + '"]');
if ($rangeEl.length) {
$rangeSelector.children('li.selected').removeClass('selected');
$rangeEl.addClass('selected');
} else {
$rangeSelector.children('li.selected').removeClass('selected');
$('li[data-range="custom"]').addClass('selected');
} }
}); } else {
var $endPicker = $("#end-date-picker").datepicker({ $rangeSelector.children('li.selected').removeClass('selected');
maxDate: 0, $('[data-range="custom"]').addClass('selected');
minDate: minDate, }
dateFormat: 'yy-mm-dd', populateCustomRange();
onSelect: function(dateText) { }
$("#date-range-end").val(dateText); if (newState.group) {
} $groupSelector.children('.selected').removeClass('selected');
}); $('li[data-group="' + newState.group + '"]').addClass('selected');
}
});
$rangeSelector.click(function(e) { $('#chart-zoomout').click(_pd);
var $target = $(e.target).parent();
var newRange = $target.attr("data-range");
if (newRange && newRange != "custom") {
$target.trigger('changeview', {range: newRange});
}
e.preventDefault();
});
$groupSelector.on('click', 'a', function(e) { $('#date-range-form').submit(
var $target = $(this).parent(), _pd(function (e) {
newGroup = $target.attr("data-group"); var start = Date.iso($('#date-range-start').val()),
end = Date.iso($('#date-range-end').val()),
$(this).trigger('changeview', { group: newGroup }); newRange = {
e.preventDefault(); custom: true,
}); start: Date.iso(start),
end: Date.iso(end),
// set controls when `changeview` is detected. };
$(window).on('changeview', function(e, newState) { $rangeSelector.trigger('changeview', { range: newRange });
if (!newState) return; $customModal.hider();
function populateCustomRange() { }),
var nRange = normalizeRange(newState.range), );
startStr = nRange.start.iso(),
endStr = nRange.end.iso();
// Trim nRange.end by one day if custom range.
if (newState.range.custom) {
nRange.end = new Date(nRange.end.getTime() - msDay);
endStr = nRange.end.iso();
}
$("#date-range-start").val(startStr);
$startPicker.datepicker("setDate", startStr);
$("#date-range-end").val(endStr);
$endPicker.datepicker("setDate", endStr);
}
if (newState.range) {
if (!newState.range.custom) {
var newRange = newState.range,
$rangeEl = $('li[data-range="' + _.escape(newRange) + '"]');
if ($rangeEl.length) {
$rangeSelector.children("li.selected")
.removeClass("selected");
$rangeEl.addClass("selected");
} else {
$rangeSelector.children("li.selected")
.removeClass("selected");
$('li[data-range="custom"]').addClass("selected");
}
} else {
$rangeSelector.children("li.selected").removeClass("selected");
$('[data-range="custom"]').addClass("selected");
}
populateCustomRange();
}
if (newState.group) {
$groupSelector.children('.selected').removeClass('selected');
$('li[data-group="' + newState.group + '"]').addClass('selected');
}
});
$("#chart-zoomout").click(_pd);
$("#date-range-form").submit(_pd(function(e) {
var start = Date.iso($("#date-range-start").val()),
end = Date.iso($("#date-range-end").val()),
newRange = {
custom: true,
start: Date.iso(start),
end: Date.iso(end)
};
$rangeSelector.trigger('changeview', {range: newRange});
$customModal.hider();
}));
})(); })();

Просмотреть файл

@ -1,304 +1,304 @@
var csv_keys = { var csv_keys = {
downloads: { downloads: {
"count": gettext('Downloads') count: gettext('Downloads'),
}, },
usage: { usage: {
"count": gettext('Daily Users') count: gettext('Daily Users'),
}, },
collections_created: { collections_created: {
'count': gettext('Collections Created') count: gettext('Collections Created'),
}, },
addons_in_use: { addons_in_use: {
'count': gettext('Add-ons in Use') count: gettext('Add-ons in Use'),
}, },
addons_created: { addons_created: {
'count': gettext('Add-ons Created') count: gettext('Add-ons Created'),
}, },
addons_downloaded: { addons_downloaded: {
'count': gettext('Add-ons Downloaded') count: gettext('Add-ons Downloaded'),
}, },
addons_updated: { addons_updated: {
'count': gettext('Add-ons Updated') count: gettext('Add-ons Updated'),
}, },
reviews_created: { reviews_created: {
'count': gettext('Reviews Written') count: gettext('Reviews Written'),
}, },
users_created: { users_created: {
'count': gettext('User Signups') count: gettext('User Signups'),
}, },
subscribers: { subscribers: {
'count': gettext('Subscribers') count: gettext('Subscribers'),
}, },
ratings: { ratings: {
'count': gettext('Ratings') count: gettext('Ratings'),
}, },
sales: { sales: {
'count': gettext('Sales') count: gettext('Sales'),
}, },
installs: { installs: {
'count': gettext('Installs') count: gettext('Installs'),
}, },
sources: { sources: {
"null" : gettext('Unknown'), null: gettext('Unknown'),
'api' : gettext('Add-ons Manager'), api: gettext('Add-ons Manager'),
'discovery-promo' : gettext('Add-ons Manager Promo'), 'discovery-promo': gettext('Add-ons Manager Promo'),
'discovery-featured' : gettext('Add-ons Manager Featured'), 'discovery-featured': gettext('Add-ons Manager Featured'),
'discovery-learnmore' : gettext('Add-ons Manager Learn More'), 'discovery-learnmore': gettext('Add-ons Manager Learn More'),
'ss' : gettext('Search Suggestions'), ss: gettext('Search Suggestions'),
'search' : gettext('Search Results'), search: gettext('Search Results'),
'homepagepromo' : gettext('Homepage Promo'), homepagepromo: gettext('Homepage Promo'),
'hp-btn-promo' : gettext('Homepage Promo'), 'hp-btn-promo': gettext('Homepage Promo'),
'hp-dl-promo' : gettext('Homepage Promo'), 'hp-dl-promo': gettext('Homepage Promo'),
'hp-hc-featured' : gettext('Homepage Featured'), 'hp-hc-featured': gettext('Homepage Featured'),
'hp-dl-featured' : gettext('Homepage Featured'), 'hp-dl-featured': gettext('Homepage Featured'),
'hp-hc-upandcoming' : gettext('Homepage Up and Coming'), 'hp-hc-upandcoming': gettext('Homepage Up and Coming'),
'hp-dl-upandcoming' : gettext('Homepage Up and Coming'), 'hp-dl-upandcoming': gettext('Homepage Up and Coming'),
'hp-dl-mostpopular' : gettext('Homepage Most Popular'), 'hp-dl-mostpopular': gettext('Homepage Most Popular'),
'dp-btn-primary' : gettext('Detail Page'), 'dp-btn-primary': gettext('Detail Page'),
'dp-btn-version' : gettext('Detail Page (bottom)'), 'dp-btn-version': gettext('Detail Page (bottom)'),
'addondetail' : gettext('Detail Page'), addondetail: gettext('Detail Page'),
'addon-detail-version' : gettext('Detail Page (bottom)'), 'addon-detail-version': gettext('Detail Page (bottom)'),
'dp-btn-devchannel' : gettext('Detail Page (Development Channel)'), 'dp-btn-devchannel': gettext('Detail Page (Development Channel)'),
'oftenusedwith' : gettext('Often Used With'), oftenusedwith: gettext('Often Used With'),
'dp-hc-oftenusedwith' : gettext('Often Used With'), 'dp-hc-oftenusedwith': gettext('Often Used With'),
'dp-dl-oftenusedwith' : gettext('Often Used With'), 'dp-dl-oftenusedwith': gettext('Often Used With'),
'dp-hc-othersby' : gettext('Others By Author'), 'dp-hc-othersby': gettext('Others By Author'),
'dp-dl-othersby' : gettext('Others By Author'), 'dp-dl-othersby': gettext('Others By Author'),
'dp-hc-dependencies' : gettext('Dependencies'), 'dp-hc-dependencies': gettext('Dependencies'),
'dp-dl-dependencies' : gettext('Dependencies'), 'dp-dl-dependencies': gettext('Dependencies'),
'dp-hc-upsell' : gettext('Upsell'), 'dp-hc-upsell': gettext('Upsell'),
'dp-dl-upsell' : gettext('Upsell'), 'dp-dl-upsell': gettext('Upsell'),
'developers' : gettext('Meet the Developer'), developers: gettext('Meet the Developer'),
'userprofile' : gettext('User Profile'), userprofile: gettext('User Profile'),
'version-history' : gettext('Version History'), 'version-history': gettext('Version History'),
'sharingapi' : gettext('Sharing'), sharingapi: gettext('Sharing'),
'category' : gettext('Category Pages'), category: gettext('Category Pages'),
'collection' : gettext('Collections'), collection: gettext('Collections'),
'cb-hc-featured' : gettext('Category Landing Featured Carousel'), 'cb-hc-featured': gettext('Category Landing Featured Carousel'),
'cb-dl-featured' : gettext('Category Landing Featured Carousel'), 'cb-dl-featured': gettext('Category Landing Featured Carousel'),
'cb-hc-toprated' : gettext('Category Landing Top Rated'), 'cb-hc-toprated': gettext('Category Landing Top Rated'),
'cb-dl-toprated' : gettext('Category Landing Top Rated'), 'cb-dl-toprated': gettext('Category Landing Top Rated'),
'cb-hc-mostpopular' : gettext('Category Landing Most Popular'), 'cb-hc-mostpopular': gettext('Category Landing Most Popular'),
'cb-dl-mostpopular' : gettext('Category Landing Most Popular'), 'cb-dl-mostpopular': gettext('Category Landing Most Popular'),
'cb-hc-recentlyadded' : gettext('Category Landing Recently Added'), 'cb-hc-recentlyadded': gettext('Category Landing Recently Added'),
'cb-dl-recentlyadded' : gettext('Category Landing Recently Added'), 'cb-dl-recentlyadded': gettext('Category Landing Recently Added'),
'cb-btn-featured' : gettext('Browse Listing Featured Sort'), 'cb-btn-featured': gettext('Browse Listing Featured Sort'),
'cb-dl-featured' : gettext('Browse Listing Featured Sort'), 'cb-dl-featured': gettext('Browse Listing Featured Sort'),
'cb-btn-users' : gettext('Browse Listing Users Sort'), 'cb-btn-users': gettext('Browse Listing Users Sort'),
'cb-dl-users' : gettext('Browse Listing Users Sort'), 'cb-dl-users': gettext('Browse Listing Users Sort'),
'cb-btn-rating' : gettext('Browse Listing Rating Sort'), 'cb-btn-rating': gettext('Browse Listing Rating Sort'),
'cb-dl-rating' : gettext('Browse Listing Rating Sort'), 'cb-dl-rating': gettext('Browse Listing Rating Sort'),
'cb-btn-created' : gettext('Browse Listing Created Sort'), 'cb-btn-created': gettext('Browse Listing Created Sort'),
'cb-dl-created' : gettext('Browse Listing Created Sort'), 'cb-dl-created': gettext('Browse Listing Created Sort'),
'cb-btn-name' : gettext('Browse Listing Name Sort'), 'cb-btn-name': gettext('Browse Listing Name Sort'),
'cb-dl-name' : gettext('Browse Listing Name Sort'), 'cb-dl-name': gettext('Browse Listing Name Sort'),
'cb-btn-popular' : gettext('Browse Listing Popular Sort'), 'cb-btn-popular': gettext('Browse Listing Popular Sort'),
'cb-dl-popular' : gettext('Browse Listing Popular Sort'), 'cb-dl-popular': gettext('Browse Listing Popular Sort'),
'cb-btn-updated' : gettext('Browse Listing Updated Sort'), 'cb-btn-updated': gettext('Browse Listing Updated Sort'),
'cb-dl-updated' : gettext('Browse Listing Updated Sort'), 'cb-dl-updated': gettext('Browse Listing Updated Sort'),
'cb-btn-hotness' : gettext('Browse Listing Up and Coming Sort'), 'cb-btn-hotness': gettext('Browse Listing Up and Coming Sort'),
'cb-dl-hotness' : gettext('Browse Listing Up and Coming Sort') 'cb-dl-hotness': gettext('Browse Listing Up and Coming Sort'),
}, },
contributions: { contributions: {
"count": gettext('Number of Contributions'), count: gettext('Number of Contributions'),
"total": gettext('Total Amount Contributed'), total: gettext('Total Amount Contributed'),
"average": gettext('Average Contribution') average: gettext('Average Contribution'),
}, },
overview: { overview: {
'downloads' : gettext('Downloads'), downloads: gettext('Downloads'),
'updates' : gettext('Daily Users') updates: gettext('Daily Users'),
}, },
app_overview: { app_overview: {
'installs': gettext('Installs'), installs: gettext('Installs'),
'sales': gettext('Sales'), sales: gettext('Sales'),
'usage': gettext('Usage') usage: gettext('Usage'),
}, },
apps : { apps: {
'{ec8030f7-c20a-464f-9b0e-13a3a9e97384}' : gettext('Firefox'), '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': gettext('Firefox'),
'{86c18b42-e466-45a9-ae7a-9b95ba6f5640}' : gettext('Mozilla'), '{86c18b42-e466-45a9-ae7a-9b95ba6f5640}': gettext('Mozilla'),
'{3550f703-e582-4d05-9a08-453d09bdfdc6}' : gettext('Thunderbird'), '{3550f703-e582-4d05-9a08-453d09bdfdc6}': gettext('Thunderbird'),
'{718e30fb-e89b-41dd-9da7-e25a45638b28}' : gettext('Sunbird'), '{718e30fb-e89b-41dd-9da7-e25a45638b28}': gettext('Sunbird'),
'{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}' : gettext('SeaMonkey'), '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}': gettext('SeaMonkey'),
'{a23983c0-fd0e-11dc-95ff-0800200c9a66}' : gettext('Fennec'), '{a23983c0-fd0e-11dc-95ff-0800200c9a66}': gettext('Fennec'),
'{aa3c5121-dab2-40e2-81ca-7ea25febc110}' : gettext('Android') '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': gettext('Android'),
}, },
chartTitle: { chartTitle: {
"overview" : [ overview: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Downloads and Daily Users, last {0} days"), gettext('Downloads and Daily Users, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Downloads and Daily Users from {0} to {1}") gettext('Downloads and Daily Users from {0} to {1}'),
], ],
"app_overview" : [ app_overview: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Installs and Daily Users, last {0} days"), gettext('Installs and Daily Users, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Installs and Daily Users from {0} to {1}") gettext('Installs and Daily Users from {0} to {1}'),
], ],
"downloads" : [ downloads: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Downloads, last {0} days"), gettext('Downloads, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Downloads from {0} to {1}") gettext('Downloads from {0} to {1}'),
], ],
"usage" : [ usage: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Daily Users, last {0} days"), gettext('Daily Users, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Daily Users from {0} to {1}") gettext('Daily Users from {0} to {1}'),
], ],
"apps" : [ apps: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Applications, last {0} days"), gettext('Applications, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Applications from {0} to {1}") gettext('Applications from {0} to {1}'),
], ],
"countries" : [ countries: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Countries, last {0} days"), gettext('Countries, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Countries from {0} to {1}") gettext('Countries from {0} to {1}'),
], ],
"os" : [ os: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Platforms, last {0} days"), gettext('Platforms, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Platforms from {0} to {1}") gettext('Platforms from {0} to {1}'),
], ],
"locales" : [ locales: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Languages, last {0} days"), gettext('Languages, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Languages from {0} to {1}") gettext('Languages from {0} to {1}'),
], ],
"versions" : [ versions: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Add-on Versions, last {0} days"), gettext('Add-on Versions, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Add-on Versions from {0} to {1}") gettext('Add-on Versions from {0} to {1}'),
], ],
"statuses" : [ statuses: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Add-on Status, last {0} days"), gettext('Add-on Status, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Add-on Status from {0} to {1}") gettext('Add-on Status from {0} to {1}'),
], ],
"sources" : [ sources: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Download Sources, last {0} days"), gettext('Download Sources, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Download Sources from {0} to {1}") gettext('Download Sources from {0} to {1}'),
], ],
"mediums" : [ mediums: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Download Mediums, last {0} days"), gettext('Download Mediums, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Download Mediums from {0} to {1}") gettext('Download Mediums from {0} to {1}'),
], ],
"contents" : [ contents: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Download Contents, last {0} days"), gettext('Download Contents, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Download Contents from {0} to {1}") gettext('Download Contents from {0} to {1}'),
], ],
"campaigns" : [ campaigns: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Download Campaigns, last {0} days"), gettext('Download Campaigns, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Download Campaigns from {0} to {1}") gettext('Download Campaigns from {0} to {1}'),
], ],
"contributions" : [ contributions: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Contributions, last {0} days"), gettext('Contributions, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Contributions from {0} to {1}") gettext('Contributions from {0} to {1}'),
], ],
"site" : [ site: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Site Metrics, last {0} days"), gettext('Site Metrics, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Site Metrics from {0} to {1}") gettext('Site Metrics from {0} to {1}'),
], ],
"addons_in_use" : [ addons_in_use: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Add-ons in Use, last {0} days"), gettext('Add-ons in Use, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Add-ons in Use from {0} to {1}") gettext('Add-ons in Use from {0} to {1}'),
], ],
"addons_downloaded" : [ addons_downloaded: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Add-ons Downloaded, last {0} days"), gettext('Add-ons Downloaded, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Add-ons Downloaded from {0} to {1}") gettext('Add-ons Downloaded from {0} to {1}'),
], ],
"addons_created" : [ addons_created: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Add-ons Created, last {0} days"), gettext('Add-ons Created, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Add-ons Created from {0} to {1}") gettext('Add-ons Created from {0} to {1}'),
], ],
"addons_updated" : [ addons_updated: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Add-ons Updated, last {0} days"), gettext('Add-ons Updated, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Add-ons Updated from {0} to {1}") gettext('Add-ons Updated from {0} to {1}'),
], ],
"reviews_created" : [ reviews_created: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Reviews Written, last {0} days"), gettext('Reviews Written, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Reviews Written from {0} to {1}") gettext('Reviews Written from {0} to {1}'),
], ],
"users_created" : [ users_created: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("User Signups, last {0} days"), gettext('User Signups, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("User Signups from {0} to {1}") gettext('User Signups from {0} to {1}'),
], ],
"collections_created" : [ collections_created: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Collections Created, last {0} days"), gettext('Collections Created, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Collections Created from {0} to {1}") gettext('Collections Created from {0} to {1}'),
], ],
"subscribers" : [ subscribers: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Subscribers, last {0} days"), gettext('Subscribers, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Subscribers from {0} to {1}") gettext('Subscribers from {0} to {1}'),
], ],
"ratings" : [ ratings: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Ratings, last {0} days"), gettext('Ratings, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Ratings from {0} to {1}") gettext('Ratings from {0} to {1}'),
], ],
"sales" : [ sales: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Sales, last {0} days"), gettext('Sales, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Sales from {0} to {1}") gettext('Sales from {0} to {1}'),
], ],
"installs" : [ installs: [
// L10n: {0} is an integer. // L10n: {0} is an integer.
gettext("Installs, last {0} days"), gettext('Installs, last {0} days'),
// L10n: both {0} and {1} are dates in YYYY-MM-DD format. // L10n: both {0} and {1} are dates in YYYY-MM-DD format.
gettext("Installs from {0} to {1}") gettext('Installs from {0} to {1}'),
] ],
}, },
aggregateLabel: { aggregateLabel: {
"downloads" : [ downloads: [
// L10n: {0} and {1} are integers. // L10n: {0} and {1} are integers.
gettext("<b>{0}</b> in last {1} days"), gettext('<b>{0}</b> in last {1} days'),
// L10n: {0} is an integer and {1} and {2} are dates in YYYY-MM-DD format. // L10n: {0} is an integer and {1} and {2} are dates in YYYY-MM-DD format.
gettext("<b>{0}</b> from {1} to {2}"), gettext('<b>{0}</b> from {1} to {2}'),
], ],
"usage" : [ usage: [
// L10n: {0} and {1} are integers. // L10n: {0} and {1} are integers.
gettext("<b>{0}</b> average in last {1} days"), gettext('<b>{0}</b> average in last {1} days'),
// L10n: {0} is an integer and {1} and {2} are dates in YYYY-MM-DD format. // L10n: {0} is an integer and {1} and {2} are dates in YYYY-MM-DD format.
gettext("<b>{0}</b> from {1} to {2}"), gettext('<b>{0}</b> from {1} to {2}'),
] ],
} },
}; };

Просмотреть файл

@ -1,113 +1,117 @@
// date management helpers // date management helpers
(function() { (function () {
// utility // utility
function pad2(n) { function pad2(n) {
var str = n.toString(); var str = n.toString();
return ('0' + str).substr(-2); return ('0' + str).substr(-2);
} }
var intervalRegex = /(-?\d+)\s*(\w)/, var intervalRegex = /(-?\d+)\s*(\w)/,
// ISO date format is used for internal representations. // ISO date format is used for internal representations.
dateRegex = /(\d{4})[^\d]?(\d{2})[^\d]?(\d{2})/; dateRegex = /(\d{4})[^\d]?(\d{2})[^\d]?(\d{2})/;
_.extend(Date.prototype, { _.extend(Date.prototype, {
forward : function(by, unit) { forward: function (by, unit) {
if (typeof by == 'string') { if (typeof by == 'string') {
var match = intervalRegex.exec(by); var match = intervalRegex.exec(by);
by = +match[1]; by = +match[1];
unit = match[2]; unit = match[2];
} }
unit = unit || 'd'; unit = unit || 'd';
switch (unit[0]) { switch (unit[0]) {
case 'h': case 'h':
this.setHours(this.getHours()+by); this.setHours(this.getHours() + by);
break; break;
case 'd': case 'd':
this.setDate(this.getDate()+by); this.setDate(this.getDate() + by);
break; break;
case 'w': case 'w':
this.setDate(this.getDate()+by*7); this.setDate(this.getDate() + by * 7);
break; break;
case 'm': case 'm':
this.setMonth(this.getMonth()+by); this.setMonth(this.getMonth() + by);
break; break;
case 'y': case 'y':
this.setFullYear(this.getFullYear()+by); this.setFullYear(this.getFullYear() + by);
break; break;
} }
return this; return this;
}, },
backward : function(by, unit) { backward: function (by, unit) {
if (typeof by == 'string') { if (typeof by == 'string') {
var match = intervalRegex.exec(by); var match = intervalRegex.exec(by);
by = +match[1]; by = +match[1];
unit = match[2]; unit = match[2];
} }
return this.forward(-by, unit); return this.forward(-by, unit);
}, },
pretty : function(del) { pretty: function (del) {
del = del || ''; del = del || '';
return [this.getFullYear(), pad2(this.getMonth()+1), pad2(this.getDate())].join(del); return [
}, this.getFullYear(),
iso : function() { pad2(this.getMonth() + 1),
return this.pretty('-'); pad2(this.getDate()),
}, ].join(del);
isAfter : function(d) { },
return this.getTime() > d.getTime(); iso: function () {
}, return this.pretty('-');
isBefore : function(d) { },
return this.getTime() < d.getTime(); isAfter: function (d) {
}, return this.getTime() > d.getTime();
latter : function(d) { },
return this.isAfter(d) ? this : d; isBefore: function (d) {
}, return this.getTime() < d.getTime();
former : function(d) { },
return this.isBefore(d) ? this : d; latter: function (d) {
}, return this.isAfter(d) ? this : d;
clone : function() { },
return new Date(this.getTime()); former: function (d) {
} return this.isBefore(d) ? this : d;
}); },
_.extend(Date, { clone: function () {
ago : function(s) { return new Date(this.getTime());
return (new Date()).backward(s); },
}, });
iso : function(s) { _.extend(Date, {
if (s instanceof Date) return s; ago: function (s) {
var d = dateRegex.exec(s); return new Date().backward(s);
if (d) { },
return new Date(d[1],d[2]-1,d[3]); iso: function (s) {
} if (s instanceof Date) return s;
} var d = dateRegex.exec(s);
}); if (d) {
_.extend(String, { return new Date(d[1], d[2] - 1, d[3]);
max : function(a,b) { }
return a > b ? a : b; },
}, });
min : function(a,b) { _.extend(String, {
return a < b ? a : b; max: function (a, b) {
} return a > b ? a : b;
}); },
min: function (a, b) {
return a < b ? a : b;
},
});
})(); })();
function forEachISODate(range, step, data, iterator, context) { function forEachISODate(range, step, data, iterator, context) {
var d = range.start.clone(); var d = range.start.clone();
for (d; d.isBefore(range.end); d.forward(step)) { for (d; d.isBefore(range.end); d.forward(step)) {
var ds = d.iso(); var ds = d.iso();
iterator.call(context, data[ds], d, ds); iterator.call(context, data[ds], d, ds);
} }
} }
function normalizeRange(range) { function normalizeRange(range) {
var ret = {}; var ret = {};
if (typeof range == "string") { if (typeof range == 'string') {
ret.start = Date.ago(range); ret.start = Date.ago(range);
ret.end = (new Date()); ret.end = new Date();
} else if (typeof range == "object") { } else if (typeof range == 'object') {
ret.start = new Date(range.start); ret.start = new Date(range.start);
ret.end = new Date(range.end); ret.end = new Date(range.end);
} else { } else {
throw "Invalid range values found."; throw 'Invalid range values found.';
} }
return ret; return ret;
} }

Просмотреть файл

@ -1,43 +1,47 @@
// Web Worker Pool // Web Worker Pool
// size is the max number of arguments // size is the max number of arguments
function WorkerPool(size) { function WorkerPool(size) {
var workers = 0, var workers = 0,
jobs = []; jobs = [];
// url: the url of the worker's js // url: the url of the worker's js
// msg: the initial message to pass to the worker // msg: the initial message to pass to the worker
// cb : the callback to recieve messages from postMessage. // cb : the callback to recieve messages from postMessage.
// return true from cb to dismiss the worker and advance the queue. // return true from cb to dismiss the worker and advance the queue.
// ctx: the context for cb.apply // ctx: the context for cb.apply
this.queueJob = function(url, msg, cb, ctx) { this.queueJob = function (url, msg, cb, ctx) {
var job = { var job = {
"url": url, url: url,
"msg": msg, msg: msg,
"cb" : cb, cb: cb,
"ctx": ctx ctx: ctx,
};
jobs.push(job);
if (workers < size) nextJob();
}; };
jobs.push(job);
if (workers < size) nextJob();
};
function nextJob() { function nextJob() {
if (jobs.length) { if (jobs.length) {
(function() { (function () {
var job = jobs.shift(), var job = jobs.shift(),
worker = new Worker(job.url); worker = new Worker(job.url);
workers++; workers++;
worker.addEventListener('message', function(e) { worker.addEventListener(
if (job.cb.call(job.ctx, e.data, worker)) { 'message',
worker.terminate(); function (e) {
worker = null; if (job.cb.call(job.ctx, e.data, worker)) {
workers--; worker.terminate();
nextJob(); worker = null;
}; workers--;
}, false); nextJob();
worker.postMessage(job.msg); }
})(); },
} false,
);
worker.postMessage(job.msg);
})();
} }
}
} }
// Simple Asynchronous Cache // Simple Asynchronous Cache
@ -49,44 +53,46 @@ function WorkerPool(size) {
// Takes one parameter: // Takes one parameter:
// * key // * key
function AsyncCache(miss, hash) { function AsyncCache(miss, hash) {
var cache = {}, var cache = {},
self = this; self = this;
hash = hash || function(key) { hash =
return key.toString(); hash ||
function (key) {
return key.toString();
}; };
// key: the key to lookup in the cache // key: the key to lookup in the cache
// cb : the method to call with the value // cb : the method to call with the value
// Takes one parameter: // Takes one parameter:
// val: the value in the cache for key // val: the value in the cache for key
// ctx: context for cb.call // ctx: context for cb.call
this.get = function(key, cb, ctx) { this.get = function (key, cb, ctx) {
var k = hash(key); var k = hash(key);
if (k in cache) { if (k in cache) {
cb.call(ctx, cache[k]); cb.call(ctx, cache[k]);
} else { } else {
miss.call(ctx, key, function(val) { miss.call(ctx, key, function (val) {
self.set(key, val); self.set(key, val);
self.get(key, cb, ctx); self.get(key, cb, ctx);
}); });
} }
}; };
// sets value for key in cache // sets value for key in cache
this.set = function(key, val) { this.set = function (key, val) {
cache[hash(key)] = val; cache[hash(key)] = val;
}; };
} }
function hashObj(o) { function hashObj(o) {
var hash = []; var hash = [];
for (var i in o) { for (var i in o) {
if (o.hasOwnProperty(i)) { if (o.hasOwnProperty(i)) {
hash.push(o[i].toString()); hash.push(o[i].toString());
}
} }
return hash.join('_'); }
return hash.join('_');
} }
/* cfg takes: /* cfg takes:
@ -99,27 +105,26 @@ function hashObj(o) {
* ctx: context from which to run all functions * ctx: context from which to run all functions
*/ */
function chunkfor(cfg) { function chunkfor(cfg) {
var position = cfg.start; var position = cfg.start;
function nextchunk() { function nextchunk() {
if (position < cfg.end) { if (position < cfg.end) {
for (
var iterator = position;
iterator < position + cfg.chunk_size * cfg.step && iterator < cfg.end;
iterator += cfg.step
) {
cfg.inner.call(cfg.ctx, iterator);
}
for (var iterator = position; position += cfg.chunk_size * cfg.step;
iterator < position+(cfg.chunk_size*cfg.step) && iterator < cfg.end;
iterator += cfg.step) {
cfg.inner.call(cfg.ctx, iterator); setTimeout(function () {
} nextchunk.call(this);
}, 0);
position += cfg.chunk_size * cfg.step; } else {
cfg.callback.call(cfg.ctx);
setTimeout( function () {
nextchunk.call(this);
}, 0);
} else {
cfg.callback.call(cfg.ctx);
}
} }
nextchunk(); }
nextchunk();
} }

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,55 +1,54 @@
$(function() { $(function () {
if ($('.primary').attr('data-report') != 'overview') return; if ($('.primary').attr('data-report') != 'overview') return;
// set up topcharts (defined in topchart.js) // set up topcharts (defined in topchart.js)
$('.toplist').topChart(); $('.toplist').topChart();
$(window).on("changeview", function(e, view) { $(window).on('changeview', function (e, view) {
$('.two-up').addClass('loading'); $('.two-up').addClass('loading');
});
// Save some requests by waiting until the graph data is ready.
$(window).on('dataready', function (e, data) {
// return;
var view = _.extend({}, data.view, { group: 'all' }),
range = normalizeRange(view.range);
// get aggregates for Daily Users and Downloads for the given time range.
$.when(z.StatsManager.getDataRange(view)).then(function (data) {
if (data.empty) {
$('#downloads-in-range, #users-in-range').text(
gettext('No data available.'),
);
} else {
// make all that data pretty.
var aggregateRow = data[data.firstIndex].data,
totalDownloads = Highcharts.numberFormat(aggregateRow.downloads, 0),
totalUsers = Highcharts.numberFormat(aggregateRow.updates, 0),
startString = range.start.iso(),
endString = range.end.iso(),
downloadFormat,
userFormat;
if (typeof view.range == 'string') {
(downloadFormat = csv_keys.aggregateLabel.downloads[0]),
(userFormat = csv_keys.aggregateLabel.usage[0]);
$('#downloads-in-range').html(
format(downloadFormat, totalDownloads, parseInt(view.range, 10)),
);
$('#users-in-range').html(
format(userFormat, totalUsers, parseInt(view.range, 10)),
);
} else {
(downloadFormat = csv_keys.aggregateLabel.downloads[1]),
(userFormat = csv_keys.aggregateLabel.usage[1]);
$('#downloads-in-range').html(
format(downloadFormat, totalDownloads, startString, endString),
);
$('#users-in-range').html(
format(userFormat, totalUsers, startString, endString),
);
}
}
$('.two-up').removeClass('loading');
}); });
// Save some requests by waiting until the graph data is ready. });
$(window).on("dataready", function(e, data) { });
// return;
var view = _.extend({}, data.view, {group: 'all'}),
range = normalizeRange(view.range);
// get aggregates for Daily Users and Downloads for the given time range.
$.when(z.StatsManager.getDataRange(view)).then(function(data) {
if (data.empty) {
$("#downloads-in-range, #users-in-range").text(gettext('No data available.'));
} else {
// make all that data pretty.
var aggregateRow = data[data.firstIndex].data,
totalDownloads = Highcharts.numberFormat(aggregateRow.downloads, 0),
totalUsers = Highcharts.numberFormat(aggregateRow.updates, 0),
startString = range.start.iso(),
endString = range.end.iso(),
downloadFormat,
userFormat;
if (typeof view.range == 'string') {
downloadFormat = csv_keys.aggregateLabel.downloads[0],
userFormat = csv_keys.aggregateLabel.usage[0];
$("#downloads-in-range").html(format(downloadFormat,
totalDownloads,
parseInt(view.range, 10)));
$("#users-in-range").html(format(userFormat,
totalUsers,
parseInt(view.range, 10)));
} else {
downloadFormat = csv_keys.aggregateLabel.downloads[1],
userFormat = csv_keys.aggregateLabel.usage[1];
$("#downloads-in-range").html(format(downloadFormat,
totalDownloads,
startString,
endString));
$("#users-in-range").html(format(userFormat,
totalUsers,
startString,
endString));
}
}
$('.two-up').removeClass('loading');
});
});
});

Просмотреть файл

@ -80,7 +80,7 @@
JSON.stringify({ JSON.stringify({
range: ssView.range, range: ssView.range,
group: ssView.group, group: ssView.group,
}) }),
); );
}); });
})(); })();
@ -107,7 +107,7 @@
var url = var url =
baseURL + baseURL +
[metric, 'day', range.start.pretty(''), range.end.pretty('')].join( [metric, 'day', range.start.pretty(''), range.end.pretty('')].join(
'-' '-',
); );
$('#export_data_csv').attr('href', url + '.csv'); $('#export_data_csv').attr('href', url + '.csv');

Просмотреть файл

@ -1,117 +1,134 @@
(function($) { (function ($) {
"use strict"; 'use strict';
$.fn.csvTable = function(cfg) { $.fn.csvTable = function (cfg) {
$(this).each(function() { $(this).each(function () {
var $self = $(this), var $self = $(this),
$table = $self.find('table'), $table = $self.find('table'),
$thead = $self.find('thead'), $thead = $self.find('thead'),
$paginator = $self.find('.paginator'), $paginator = $self.find('.paginator'),
pageSize = 14, pageSize = 14,
pages = {}, pages = {},
metric = $('.primary').attr('data-report'), metric = $('.primary').attr('data-report'),
currentPage; currentPage;
$(document).ready(init); $(document).ready(init);
function init() { function init() {
gotoPage(0); gotoPage(0);
$paginator.on('click', '.next', _pd(function() { $paginator.on(
if ($(this).hasClass('disabled')) return; 'click',
gotoPage(currentPage+1); '.next',
})); _pd(function () {
$paginator.on('click', '.prev', _pd(function() { if ($(this).hasClass('disabled')) return;
if ($(this).hasClass('disabled')) return; gotoPage(currentPage + 1);
gotoPage(currentPage-1); }),
})); );
} $paginator.on(
'click',
'.prev',
_pd(function () {
if ($(this).hasClass('disabled')) return;
gotoPage(currentPage - 1);
}),
);
}
function gotoPage(page) { function gotoPage(page) {
if (page < 0) { if (page < 0) {
page = 0; page = 0;
} }
$paginator.find('.prev').toggleClass('disabled', page==0); $paginator.find('.prev').toggleClass('disabled', page == 0);
if (pages[page]) { if (pages[page]) {
showPage(page); showPage(page);
} else { } else {
$self.parent().addClass('loading'); $self.parent().addClass('loading');
$.when(getPage(page)) $.when(getPage(page)).then(function () {
.then(function() { showPage(page);
showPage(page); $self.parent().removeClass('loading');
$self.parent().removeClass('loading'); getPage(page + 1);
getPage(page+1); getPage(page - 1);
getPage(page-1); });
}); }
} }
}
function showPage(page) { function showPage(page) {
var p = pages[page]; var p = pages[page];
if (p) { if (p) {
$table.find('tbody').hide(); $table.find('tbody').hide();
p.el.show(); p.el.show();
$thead.empty().html(p.head); $thead.empty().html(p.head);
} }
currentPage = page; currentPage = page;
} }
function getPage(page) { function getPage(page) {
if (pages[page] || page < 0) return; if (pages[page] || page < 0) return;
var $def = $.Deferred(), var $def = $.Deferred(),
range = { range = {
end : Date.ago(pageSize * page + 'days'), end: Date.ago(pageSize * page + 'days'),
start : Date.ago(pageSize * page + pageSize + 'days') start: Date.ago(pageSize * page + pageSize + 'days'),
}, },
view = { view = {
metric : metric, metric: metric,
group : 'day', group: 'day',
range : range range: range,
}; };
$.when(z.StatsManager.getDataRange(view)) $.when(z.StatsManager.getDataRange(view)).then(function (data) {
.then(function(data) { var fields = z.StatsManager.getAvailableFields(view),
var fields = z.StatsManager.getAvailableFields(view), newBody = '<tbody>',
newBody = '<tbody>', newPage = {},
newPage = {}, newHead = '<tr><th>' + gettext('Date') + '</th>',
newHead = '<tr><th>' + gettext('Date') + '</th>', row;
row;
_.each(fields, function(f) { _.each(fields, function (f) {
var id = f.split('|').pop(), var id = f.split('|').pop(),
prettyName = _.escape(z.StatsManager.getPrettyName(metric, id)), prettyName = _.escape(z.StatsManager.getPrettyName(metric, id)),
trimmedPrettyName = (prettyName.length > 32) ? (prettyName.substr(0, 32) + '...') : prettyName; trimmedPrettyName =
newHead += format('<th title="{0}">', prettyName); prettyName.length > 32
newHead += trimmedPrettyName; ? prettyName.substr(0, 32) + '...'
newHead += '</th>'; : prettyName;
}); newHead += format('<th title="{0}">', prettyName);
newHead += trimmedPrettyName;
newHead += '</th>';
});
var d = range.end.clone().backward('1 day'), var d = range.end.clone().backward('1 day'),
lastRowDate = range.start.clone().backward('1 day'); lastRowDate = range.start.clone().backward('1 day');
for (; lastRowDate.isBefore(d); d.backward('1 day')) { for (; lastRowDate.isBefore(d); d.backward('1 day')) {
row = data[d.iso()] || {}; row = data[d.iso()] || {};
newBody += '<tr>'; newBody += '<tr>';
newBody += '<th>' + Highcharts.dateFormat('%a, %b %e, %Y', Date.iso(d)) + "</th>"; newBody +=
_.each(fields, function(f) { '<th>' +
newBody += '<td>'; Highcharts.dateFormat('%a, %b %e, %Y', Date.iso(d)) +
if (metric == 'contributions' && f != 'count') { '</th>';
newBody += '$' + Highcharts.numberFormat(z.StatsManager.getField(row, f), 2); _.each(fields, function (f) {
} else { newBody += '<td>';
newBody += Highcharts.numberFormat(z.StatsManager.getField(row, f),0); if (metric == 'contributions' && f != 'count') {
} newBody +=
newBody += '</td>'; '$' +
}); Highcharts.numberFormat(z.StatsManager.getField(row, f), 2);
newBody += '</tr>'; } else {
} newBody += Highcharts.numberFormat(
newBody += '</tbody>'; z.StatsManager.getField(row, f),
0,
);
}
newBody += '</td>';
});
newBody += '</tr>';
}
newBody += '</tbody>';
newPage.el = $(newBody); newPage.el = $(newBody);
newPage.head = newHead; newPage.head = newHead;
$table.append(newPage.el); $table.append(newPage.el);
pages[page] = newPage; pages[page] = newPage;
$def.resolve(); $def.resolve();
});
return $def;
}
}); });
}; return $def;
}
});
};
})(jQuery); })(jQuery);

Просмотреть файл

@ -1,141 +1,158 @@
(function($) { (function ($) {
// "use strict"; // "use strict";
var baseConfig = { var baseConfig = {
chart: { chart: {
backgroundColor: null backgroundColor: null,
}, },
title: { title: {
text: null text: null,
}, },
plotArea: { plotArea: {
shadow: null, shadow: null,
borderWidth: null borderWidth: null,
}, },
tooltip: { tooltip: {
enabled: false enabled: false,
}, },
plotOptions: { plotOptions: {
pie: { pie: {
allowPointSelect: false, allowPointSelect: false,
dataLabels: { dataLabels: {
enabled: false, enabled: false,
color: '#333' color: '#333',
}, },
animation: false, animation: false,
size:190 size: 190,
} },
}, },
credits: {enabled:false}, credits: { enabled: false },
legend: { legend: {
enabled:false enabled: false,
}, },
series: [{ series: [
type: 'pie' {
}] type: 'pie',
}; },
],
};
$.fn.topChart = function(cfg) { $.fn.topChart = function (cfg) {
$(this).each(function() { $(this).each(function () {
var $self = $(this), var $self = $(this),
$win = $(window), $win = $(window),
$chart = $self.find('.piechart'), $chart = $self.find('.piechart'),
hChart, hChart,
$table = $self.find('table'), $table = $self.find('table'),
metric = $table.attr('data-metric'), metric = $table.attr('data-metric'),
view = { view = {
'metric': metric, metric: metric,
'group' : 'all' group: 'all',
}; };
// reload the data when the view's range is modified. // reload the data when the view's range is modified.
$win.on('changeview', function(e, newView) { $win.on('changeview', function (e, newView) {
// we only want to respond to changes in range. // we only want to respond to changes in range.
if (!newView.range) return; if (!newView.range) return;
$self.addClass('loading'); $self.addClass('loading');
$self.removeClass('nodata'); $self.removeClass('nodata');
_.extend(view, {'range' : normalizeRange(newView.range)}); _.extend(view, { range: normalizeRange(newView.range) });
$.when(z.StatsManager.getDataRange(view)) $.when(z.StatsManager.getDataRange(view)).then(function (data) {
.then(function(data) { generateRankedList(data, render);
generateRankedList(data, render);
});
});
// We take the data (aggregated to one row)
function generateRankedList(data, done) {
if (data.empty) {
$self.removeClass('loading');
$self.addClass('nodata');
if (hChart && hChart.destroy) hChart.destroy();
$table.html('');
return;
}
var totalValue = 0,
otherValue = 0;
data = data[data.firstIndex].data;
if (_.isEmpty(data)) return;
// Sum all fields.
_.each(data, function(val) {
totalValue += val;
});
// Convert all fields to percentages and prettify names.
var rankedList = _.map(data, function(val, key) {
var field = key.split("|").slice(-1)[0];
return [
z.StatsManager.getPrettyName(metric, field),
val,
(val / totalValue) * 100
];
});
// Sort by value.
rankedList = _.sortBy(rankedList, function(a) {
return -a[1];
});
// Calculate the 'Other' percentage
for (var i=5; i<rankedList.length; i++) {
otherValue += rankedList[i][1];
}
// Take the top 5 values and append an 'Other' row.
rankedList = rankedList.slice(0,5);
rankedList.push([
gettext('Other'),
otherValue,
(otherValue / totalValue) * 100
]);
// Move on with our lives.
done(rankedList);
}
var tableRow = template("<tr><td>{0}</td><td title=\"{3}\">{1}</td><td>({2}%)</td></tr>");
function render(data) {
var newBody = "<tbody>";
_.each(data, function(row) {
var title = row[0];
var raw_value = row[1];
var value = Highcharts.numberFormat(raw_value, raw_value < 10 ? 1 : 0);
var percent = Highcharts.numberFormat(row[2], 0);
if (percent < 1) {
percent = "<1";
}
newBody += tableRow([title, value, percent, raw_value]);
});
newBody += "</tbody>";
$table.html(newBody);
// set up chart.
var newConfig = _.clone(baseConfig),
row;
newConfig.chart.renderTo = $chart[0];
newConfig.series[0].data = _.map(data, function(r) { return r.slice(0,2); });
hChart = new Highcharts.Chart(newConfig);
for (i = 0; i < data.length; i++) {
row = $table.find('tr').eq(i);
row.children().eq(0).append($("<b class='seriesdot' style='background:" + hChart.series[0].data[i].color + "'>&nbsp;</b>"));
}
$self.removeClass('loading');
}
}); });
}; });
// We take the data (aggregated to one row)
function generateRankedList(data, done) {
if (data.empty) {
$self.removeClass('loading');
$self.addClass('nodata');
if (hChart && hChart.destroy) hChart.destroy();
$table.html('');
return;
}
var totalValue = 0,
otherValue = 0;
data = data[data.firstIndex].data;
if (_.isEmpty(data)) return;
// Sum all fields.
_.each(data, function (val) {
totalValue += val;
});
// Convert all fields to percentages and prettify names.
var rankedList = _.map(data, function (val, key) {
var field = key.split('|').slice(-1)[0];
return [
z.StatsManager.getPrettyName(metric, field),
val,
(val / totalValue) * 100,
];
});
// Sort by value.
rankedList = _.sortBy(rankedList, function (a) {
return -a[1];
});
// Calculate the 'Other' percentage
for (var i = 5; i < rankedList.length; i++) {
otherValue += rankedList[i][1];
}
// Take the top 5 values and append an 'Other' row.
rankedList = rankedList.slice(0, 5);
rankedList.push([
gettext('Other'),
otherValue,
(otherValue / totalValue) * 100,
]);
// Move on with our lives.
done(rankedList);
}
var tableRow = template(
'<tr><td>{0}</td><td title="{3}">{1}</td><td>({2}%)</td></tr>',
);
function render(data) {
var newBody = '<tbody>';
_.each(data, function (row) {
var title = row[0];
var raw_value = row[1];
var value = Highcharts.numberFormat(
raw_value,
raw_value < 10 ? 1 : 0,
);
var percent = Highcharts.numberFormat(row[2], 0);
if (percent < 1) {
percent = '<1';
}
newBody += tableRow([title, value, percent, raw_value]);
});
newBody += '</tbody>';
$table.html(newBody);
// set up chart.
var newConfig = _.clone(baseConfig),
row;
newConfig.chart.renderTo = $chart[0];
newConfig.series[0].data = _.map(data, function (r) {
return r.slice(0, 2);
});
hChart = new Highcharts.Chart(newConfig);
for (i = 0; i < data.length; i++) {
row = $table.find('tr').eq(i);
row
.children()
.eq(0)
.append(
$(
"<b class='seriesdot' style='background:" +
hChart.series[0].data[i].color +
"'>&nbsp;</b>",
),
);
}
$self.removeClass('loading');
}
});
};
})(jQuery); })(jQuery);

Просмотреть файл

@ -1,15 +1,15 @@
$.fn.highlightTerm = function(val) { $.fn.highlightTerm = function (val) {
// If an item starts with `val`, wrap the matched text with boldness. // If an item starts with `val`, wrap the matched text with boldness.
val = val.replace(/[^\w\s]/gi, ''); val = val.replace(/[^\w\s]/gi, '');
var pat = new RegExp(val, 'gi'); var pat = new RegExp(val, 'gi');
this.each(function() { this.each(function () {
var $this = $(this), var $this = $(this),
txt = $this.html(), txt = $this.html(),
matchedTxt = txt.replace(pat, '<b>$&</b>'); matchedTxt = txt.replace(pat, '<b>$&</b>');
if (txt != matchedTxt) { if (txt != matchedTxt) {
$this.html(matchedTxt); $this.html(matchedTxt);
} }
}); });
}; };
/* /*
@ -22,205 +22,232 @@ $.fn.highlightTerm = function(val) {
* Optional: * Optional:
* searchType - possible values are 'AMO', 'MKT' * searchType - possible values are 'AMO', 'MKT'
*/ */
$.fn.searchSuggestions = function($results, processCallback, searchType) { $.fn.searchSuggestions = function ($results, processCallback, searchType) {
var $self = this, var $self = this,
$form = $self.closest('form'); $form = $self.closest('form');
if (!$results.length) { if (!$results.length) {
return; return;
} }
var cat = $results.attr('data-cat'); var cat = $results.attr('data-cat');
if (searchType == 'AMO') { if (searchType == 'AMO') {
// Some base elements that we don't want to keep creating on the fly. // Some base elements that we don't want to keep creating on the fly.
var msg; var msg;
if (cat == 'themes') { if (cat == 'themes') {
msg = gettext('Search themes for <b>{0}</b>'); msg = gettext('Search themes for <b>{0}</b>');
} else if (cat == 'apps') { } else if (cat == 'apps') {
msg = gettext('Search apps for <b>{0}</b>'); msg = gettext('Search apps for <b>{0}</b>');
} else {
msg = gettext('Search add-ons for <b>{0}</b>');
}
var base = template('<div class="wrap">' +
'<p><a class="sel" href="#"><span>{msg}</span></a></p><ul></ul>' +
'</div>');
$results.html(base({'msg': msg}));
} else if (searchType == 'MKT') {
$results.html('<div class="wrap"><ul></ul></div>');
}
// Control keys that shouldn't trigger new requests.
var ignoreKeys = [
z.keys.SHIFT, z.keys.CONTROL, z.keys.ALT, z.keys.PAUSE,
z.keys.CAPS_LOCK, z.keys.ESCAPE, z.keys.ENTER,
z.keys.PAGE_UP, z.keys.PAGE_DOWN,
z.keys.LEFT, z.keys.UP, z.keys.RIGHT, z.keys.DOWN,
z.keys.HOME, z.keys.END,
z.keys.COMMAND, z.keys.WINDOWS_RIGHT, z.keys.COMMAND_RIGHT,
z.keys.WINDOWS_LEFT_OPERA, z.keys.WINDOWS_RIGHT_OPERA, z.keys.APPLE
];
var gestureKeys = [z.keys.ESCAPE, z.keys.UP, z.keys.DOWN];
function pageUp() {
// Select the first element.
$results.find('.sel').removeClass('sel');
$results.removeClass('sel');
$results.find('a:first').addClass('sel');
}
function pageDown() {
// Select the last element.
$results.find('.sel').removeClass('sel');
$results.removeClass('sel');
$results.find('a:last').addClass('sel');
}
function dismissHandler() {
$results.removeClass('visible sel');
if (searchType == 'MKT') {
$('#site-header').removeClass('suggestions');
if (z.capabilities.mobile && $('body.home').length === 0) {
z.body.removeClass('show-search');
}
}
}
function gestureHandler(e) {
// Bail if the results are hidden or if we have a non-gesture key
// or if we have a alt/ctrl/meta/shift keybinding.
if (!$results.hasClass('visible') ||
$.inArray(e.which, gestureKeys) < 0 ||
e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
$results.trigger('keyIgnored');
return;
}
e.preventDefault();
if (e.which == z.keys.ESCAPE) {
dismissHandler();
} else if (e.which == z.keys.UP || e.which == z.keys.DOWN) {
var $sel = $results.find('.sel'),
$elems = $results.find('a'),
i = $elems.index($sel.get(0));
if ($sel.length && i >= 0) {
if (e.which == z.keys.UP) {
// Clamp the value so it goes to the previous row
// but never goes beyond the first row.
i = Math.max(0, i - 1);
} else {
// Clamp the value so it goes to the next row
// but never goes beyond the last row.
i = Math.min(i + 1, $elems.length - 1);
}
} else {
i = 0;
}
$sel.removeClass('sel');
$elems.eq(i).addClass('sel');
$results.addClass('sel').trigger('selectedRowUpdate', [i]);
}
}
function inputHandler(e) {
if (e.type === 'paste') {
pasting = true;
}
var val = escape_($self.val());
if (val.length < 3) {
$results.filter('.visible').removeClass('visible');
return;
}
// Required data to send to the callback.
var settings = {
'$results': $results,
'$form': $form,
'searchTerm': val
};
// Optional data for callback.
if (searchType == 'AMO' || searchType == 'MKT') {
settings['category'] = cat;
}
if (((e.type === 'keyup' && typeof e.which === 'undefined') ||
$.inArray(e.which, ignoreKeys) >= 0) && !pasting) {
$results.trigger('inputIgnored');
} else {
// XHR call and populate suggestions.
processCallback(settings);
}
pasting = false;
}
var pollVal = 0,
pasting = false;
if (z.capabilities.touch) {
$self.focus(function() {
// If we've already got a timer, clear it.
if (pollVal !== 0) {
clearInterval(pollVal);
}
pollVal = setInterval(function() {
gestureHandler($self);
inputHandler($self);
return;
}, 150);
});
} else { } else {
$self.on('keydown', gestureHandler) msg = gettext('Search add-ons for <b>{0}</b>');
.on('keyup paste', _.throttle(inputHandler, 250)); }
var base = template(
'<div class="wrap">' +
'<p><a class="sel" href="#"><span>{msg}</span></a></p><ul></ul>' +
'</div>',
);
$results.html(base({ msg: msg }));
} else if (searchType == 'MKT') {
$results.html('<div class="wrap"><ul></ul></div>');
}
// Control keys that shouldn't trigger new requests.
var ignoreKeys = [
z.keys.SHIFT,
z.keys.CONTROL,
z.keys.ALT,
z.keys.PAUSE,
z.keys.CAPS_LOCK,
z.keys.ESCAPE,
z.keys.ENTER,
z.keys.PAGE_UP,
z.keys.PAGE_DOWN,
z.keys.LEFT,
z.keys.UP,
z.keys.RIGHT,
z.keys.DOWN,
z.keys.HOME,
z.keys.END,
z.keys.COMMAND,
z.keys.WINDOWS_RIGHT,
z.keys.COMMAND_RIGHT,
z.keys.WINDOWS_LEFT_OPERA,
z.keys.WINDOWS_RIGHT_OPERA,
z.keys.APPLE,
];
var gestureKeys = [z.keys.ESCAPE, z.keys.UP, z.keys.DOWN];
function pageUp() {
// Select the first element.
$results.find('.sel').removeClass('sel');
$results.removeClass('sel');
$results.find('a:first').addClass('sel');
}
function pageDown() {
// Select the last element.
$results.find('.sel').removeClass('sel');
$results.removeClass('sel');
$results.find('a:last').addClass('sel');
}
function dismissHandler() {
$results.removeClass('visible sel');
if (searchType == 'MKT') {
$('#site-header').removeClass('suggestions');
if (z.capabilities.mobile && $('body.home').length === 0) {
z.body.removeClass('show-search');
}
}
}
function gestureHandler(e) {
// Bail if the results are hidden or if we have a non-gesture key
// or if we have a alt/ctrl/meta/shift keybinding.
if (
!$results.hasClass('visible') ||
$.inArray(e.which, gestureKeys) < 0 ||
e.altKey ||
e.ctrlKey ||
e.metaKey ||
e.shiftKey
) {
$results.trigger('keyIgnored');
return;
}
e.preventDefault();
if (e.which == z.keys.ESCAPE) {
dismissHandler();
} else if (e.which == z.keys.UP || e.which == z.keys.DOWN) {
var $sel = $results.find('.sel'),
$elems = $results.find('a'),
i = $elems.index($sel.get(0));
if ($sel.length && i >= 0) {
if (e.which == z.keys.UP) {
// Clamp the value so it goes to the previous row
// but never goes beyond the first row.
i = Math.max(0, i - 1);
} else {
// Clamp the value so it goes to the next row
// but never goes beyond the last row.
i = Math.min(i + 1, $elems.length - 1);
}
} else {
i = 0;
}
$sel.removeClass('sel');
$elems.eq(i).addClass('sel');
$results.addClass('sel').trigger('selectedRowUpdate', [i]);
}
}
function inputHandler(e) {
if (e.type === 'paste') {
pasting = true;
}
var val = escape_($self.val());
if (val.length < 3) {
$results.filter('.visible').removeClass('visible');
return;
} }
function clearCurrentSuggestions(e) { // Required data to send to the callback.
var settings = {
$results: $results,
$form: $form,
searchTerm: val,
};
// Optional data for callback.
if (searchType == 'AMO' || searchType == 'MKT') {
settings['category'] = cat;
}
if (
((e.type === 'keyup' && typeof e.which === 'undefined') ||
$.inArray(e.which, ignoreKeys) >= 0) &&
!pasting
) {
$results.trigger('inputIgnored');
} else {
// XHR call and populate suggestions.
processCallback(settings);
}
pasting = false;
}
var pollVal = 0,
pasting = false;
if (z.capabilities.touch) {
$self.focus(function () {
// If we've already got a timer, clear it.
if (pollVal !== 0) {
clearInterval(pollVal); clearInterval(pollVal);
// Delay dismissal to allow for click events to happen on }
// results. If we call it immediately, results get hidden pollVal = setInterval(function () {
// before the click events can happen. gestureHandler($self);
_.delay(dismissHandler, 250); inputHandler($self);
$self.trigger('dismissed'); return;
}, 150);
});
} else {
$self
.on('keydown', gestureHandler)
.on('keyup paste', _.throttle(inputHandler, 250));
}
function clearCurrentSuggestions(e) {
clearInterval(pollVal);
// Delay dismissal to allow for click events to happen on
// results. If we call it immediately, results get hidden
// before the click events can happen.
_.delay(dismissHandler, 250);
$self.trigger('dismissed');
}
$self.blur(clearCurrentSuggestions);
$form.submit(function (e) {
$self.blur();
clearCurrentSuggestions(e);
});
$results.find('.sel').click(function (e) {
e.preventDefault();
$form.submit();
});
$results
.on('mouseenter mouseleave', 'li, p', function () {
$results.find('.sel').removeClass('sel');
$results.addClass('sel');
$(this).find('a').addClass('sel');
})
.on('click', 'a', function () {
clearCurrentSuggestions();
$self.val('');
});
$results.on('highlight', function (e, val) {
// If an item starts with `val`, wrap the matched text with boldness.
$results.find('ul a span').highlightTerm(val);
$results.addClass('visible');
if (!$results.find('.sel').length) {
pageUp();
} }
});
$self.blur(clearCurrentSuggestions); $results.on('dismiss', clearCurrentSuggestions);
$form.submit(function(e) {
$self.blur();
clearCurrentSuggestions(e);
});
$results.find('.sel').click(function(e) { $(document).keyup(function (e) {
e.preventDefault(); if (fieldFocused(e)) {
$form.submit(); return;
}); }
if (e.which == 83) {
$self.focus();
}
});
$results.on('mouseenter mouseleave', 'li, p', function() { return this;
$results.find('.sel').removeClass('sel');
$results.addClass('sel');
$(this).find('a').addClass('sel');
}).on('click', 'a', function() {
clearCurrentSuggestions();
$self.val('');
});
$results.on('highlight', function(e, val) {
// If an item starts with `val`, wrap the matched text with boldness.
$results.find('ul a span').highlightTerm(val);
$results.addClass('visible');
if (!$results.find('.sel').length) {
pageUp();
}
});
$results.on('dismiss', clearCurrentSuggestions);
$(document).keyup(function(e) {
if (fieldFocused(e)) {
return;
}
if (e.which == 83) {
$self.focus();
}
});
return this;
}; };

Просмотреть файл

@ -1,69 +1,82 @@
$(function() { $(function () {
// When I click on the avatar, append `#id=<id>` to the URL. // When I click on the avatar, append `#id=<id>` to the URL.
$('.user-avatar img').click(_pd(function(e) { $('.user-avatar img').click(
window.location.hash = 'id=' + $('.user-avatar').data('user-id'); _pd(function (e) {
e.stopPropagation(); window.location.hash = 'id=' + $('.user-avatar').data('user-id');
})); e.stopPropagation();
}),
);
$('#report-user-modal').modal('#report-user-abuse', {delegate: '#page'}); $('#report-user-modal').modal('#report-user-abuse', { delegate: '#page' });
if($('#user_edit').exists()) { if ($('#user_edit').exists()) {
$('.more-all, .more-none').click(_pd(function() { $('.more-all, .more-none').click(
var $this = $(this); _pd(function () {
$this.closest('li').find('input:not([disabled])').prop('checked', $this.hasClass('more-all')); var $this = $(this);
})); $this
.closest('li')
.find('input:not([disabled])')
.prop('checked', $this.hasClass('more-all'));
}),
);
}
// Hide change password box
$('#acct-password').hide();
$('#change-acct-password').click(
_pd(function () {
$('#acct-password').fadeIn();
$('#id_oldpassword').focus();
$(this).closest('li').hide();
}),
);
// Show password box if there's an error in it.
$('#acct-password .errorlist li').exists(function () {
$('#acct-password').show();
$('#change-acct-password').closest('li').hide();
});
// Display image inline
var $avatar = $('.profile-photo .avatar'),
$a = $('<a>', {
text: gettext('Use original'),
class: 'use-original delete',
href: '#',
}).hide();
$avatar.attr('data-original', $avatar.attr('src'));
function use_original() {
$('.use-original').hide();
$('#id_photo').val('');
$avatar.attr('src', $avatar.attr('data-original'));
}
$a.click(_pd(use_original));
$avatar.closest('li').append($a);
$('#id_photo').change(function () {
var $li = $(this).closest('li'),
file = $(this)[0].files[0],
file_name = file.name || file.fileName;
$li.find('.errorlist').remove();
if (!file_name.match(/\.(jpg|png|jpeg)$/i)) {
$ul = $('<ul>', { class: 'errorlist' });
$ul.append(
$('<li>', { text: gettext('Images must be either PNG or JPG.') }),
);
$li.append($ul);
use_original();
return;
} }
var img = $(this).objectUrl();
// Hide change password box if (img) {
$('#acct-password').hide(); $a.css('display', 'block');
$('#change-acct-password').click(_pd(function() { $avatar.attr('src', img);
$('#acct-password').fadeIn();
$('#id_oldpassword').focus();
$(this).closest('li').hide();
}));
// Show password box if there's an error in it.
$('#acct-password .errorlist li').exists(function() {
$('#acct-password').show();
$('#change-acct-password').closest('li').hide();
});
// Display image inline
var $avatar = $('.profile-photo .avatar'),
$a = $('<a>', {'text': gettext('Use original'), 'class': 'use-original delete', 'href': '#'}).hide();
$avatar.attr('data-original', $avatar.attr('src'));
function use_original() {
$('.use-original').hide();
$('#id_photo').val("");
$avatar.attr('src', $avatar.attr('data-original'));
} }
$a.click(_pd(use_original)); });
$avatar.closest('li').append($a);
$('#id_photo').change(function() {
var $li = $(this).closest('li'),
file = $(this)[0].files[0],
file_name = file.name || file.fileName;
$li.find('.errorlist').remove();
if(!file_name.match(/\.(jpg|png|jpeg)$/i)) {
$ul = $('<ul>', {'class': 'errorlist'});
$ul.append($('<li>', {'text': gettext('Images must be either PNG or JPG.')}));
$li.append($ul);
use_original();
return;
}
var img = $(this).objectUrl();
if(img) {
$a.css('display', 'block');
$avatar.attr('src', img);
}
});
}); });
// Hijack "Admin / Editor Log in" context menuitem. // Hijack "Admin / Editor Log in" context menuitem.
$('#admin-login').click(function() { $('#admin-login').click(function () {
window.location = $(this).attr('data-url'); window.location = $(this).attr('data-url');
}); });

Просмотреть файл

@ -2,112 +2,119 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
;(function() { (function () {
'use strict'; 'use strict';
// !! this file assumes only one signup form per page !! // !! this file assumes only one signup form per page !!
var newsletterForm = document.getElementById('newsletter_form'); var newsletterForm = document.getElementById('newsletter_form');
var newsletterWrapper = document.getElementById('newsletter_wrap'); var newsletterWrapper = document.getElementById('newsletter_wrap');
// handle errors // handle errors
var errorArray = []; var errorArray = [];
var newsletterErrors = document.getElementById('newsletter_errors'); var newsletterErrors = document.getElementById('newsletter_errors');
function newsletterError(e) { function newsletterError(e) {
var errorList = document.createElement('ul'); var errorList = document.createElement('ul');
if(errorArray.length) { if (errorArray.length) {
for (var i = 0; i < errorArray.length; i++) { for (var i = 0; i < errorArray.length; i++) {
var item = document.createElement('li'); var item = document.createElement('li');
item.appendChild(document.createTextNode(errorArray[i])); item.appendChild(document.createTextNode(errorArray[i]));
errorList.appendChild(item); errorList.appendChild(item);
} }
} else {
// no error messages, forward to server for better troubleshooting
newsletterForm.setAttribute('data-skip-xhr', true);
newsletterForm.submit();
}
newsletterErrors.appendChild(errorList);
newsletterErrors.style.display = 'block';
}
// show sucess message
function newsletterThanks() {
var thanks = document.getElementById('newsletter_thanks');
// show thanks message
thanks.style.display = 'block';
}
// XHR subscribe; handle errors; display thanks message on success.
function newsletterSubscribe(evt) {
var skipXHR = newsletterForm.getAttribute('data-skip-xhr');
if (skipXHR) {
return true;
}
evt.preventDefault();
evt.stopPropagation();
// new submission, clear old errors
errorArray = [];
newsletterErrors.style.display = 'none';
while (newsletterErrors.firstChild) {
newsletterErrors.removeChild(newsletterErrors.firstChild);
}
var fmt = document.getElementById('fmt').value;
var email = document.getElementById('email').value;
var newsletter = document.getElementById('newsletters').value;
var privacy = document.querySelector('input[name="privacy"]:checked')
? '&privacy=true'
: '';
var params =
'email=' +
encodeURIComponent(email) +
'&newsletters=' +
newsletter +
privacy +
'&fmt=' +
fmt +
'&source_url=' +
encodeURIComponent(document.location.href);
var xhr = new XMLHttpRequest();
xhr.onload = function (r) {
if (r.target.status >= 200 && r.target.status < 300) {
// response is null if handled by service worker
if (response === null) {
newsletterError(new Error());
return;
}
var response = r.target.response;
if (response.success === true) {
newsletterForm.style.display = 'none';
newsletterThanks();
} else { } else {
// no error messages, forward to server for better troubleshooting if (response.errors) {
newsletterForm.setAttribute('data-skip-xhr', true); for (var i = 0; i < response.errors.length; i++) {
newsletterForm.submit(); errorArray.push(response.errors[i]);
}
newsletterErrors.appendChild(errorList);
newsletterErrors.style.display = 'block';
}
// show sucess message
function newsletterThanks() {
var thanks = document.getElementById('newsletter_thanks');
// show thanks message
thanks.style.display = 'block';
}
// XHR subscribe; handle errors; display thanks message on success.
function newsletterSubscribe(evt) {
var skipXHR = newsletterForm.getAttribute('data-skip-xhr');
if (skipXHR) {
return true;
}
evt.preventDefault();
evt.stopPropagation();
// new submission, clear old errors
errorArray = [];
newsletterErrors.style.display = 'none';
while (newsletterErrors.firstChild) {
newsletterErrors.removeChild(newsletterErrors.firstChild);
}
var fmt = document.getElementById('fmt').value;
var email = document.getElementById('email').value;
var newsletter = document.getElementById('newsletters').value;
var privacy = document.querySelector('input[name="privacy"]:checked') ? '&privacy=true' : '';
var params = 'email=' + encodeURIComponent(email) +
'&newsletters=' + newsletter +
privacy +
'&fmt=' + fmt +
'&source_url=' + encodeURIComponent(document.location.href);
var xhr = new XMLHttpRequest();
xhr.onload = function(r) {
if (r.target.status >= 200 && r.target.status < 300) {
// response is null if handled by service worker
if(response === null) {
newsletterError(new Error());
return;
}
var response = r.target.response;
if (response.success === true) {
newsletterForm.style.display = 'none';
newsletterThanks();
} else {
if(response.errors) {
for (var i = 0; i < response.errors.length; i++) {
errorArray.push(response.errors[i]);
}
}
newsletterError(new Error());
}
} else {
newsletterError(new Error());
} }
}; }
newsletterError(new Error());
}
} else {
newsletterError(new Error());
}
};
xhr.onerror = function(e) { xhr.onerror = function (e) {
newsletterError(e); newsletterError(e);
}; };
var url = newsletterForm.getAttribute('action'); var url = newsletterForm.getAttribute('action');
xhr.open('POST', url, true); xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('X-Requested-With','XMLHttpRequest'); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.timeout = 5000; xhr.timeout = 5000;
xhr.ontimeout = newsletterError; xhr.ontimeout = newsletterError;
xhr.responseType = 'json'; xhr.responseType = 'json';
xhr.send(params); xhr.send(params);
return false; return false;
} }
newsletterForm.addEventListener('submit', newsletterSubscribe, false); newsletterForm.addEventListener('submit', newsletterSubscribe, false);
})(); })();

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -6,21 +6,25 @@
* >>> format('{x}', {x: 1}) * >>> format('{x}', {x: 1})
* "1" * "1"
*/ */
var format = (function() { var format = (function () {
var re = /\{([^}]+)\}/g; var re = /\{([^}]+)\}/g;
return function(s, args) { return function (s, args) {
if (!s) { if (!s) {
throw "Format string is empty!"; throw 'Format string is empty!';
} }
if (!args) return; if (!args) return;
if (!(args instanceof Array || args instanceof Object)) if (!(args instanceof Array || args instanceof Object))
args = Array.prototype.slice.call(arguments, 1); args = Array.prototype.slice.call(arguments, 1);
return s.replace(re, function(_, match){ return args[match]; }); return s.replace(re, function (_, match) {
}; return args[match];
});
};
})(); })();
function template(s) { function template(s) {
if (!s) { if (!s) {
throw "Template string is empty!"; throw 'Template string is empty!';
} }
return function(args) { return format(s, args); }; return function (args) {
return format(s, args);
};
} }

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,106 +1,120 @@
/** /**
* hoverIntent is similar to jQuery's built-in "hover" function except that * hoverIntent is similar to jQuery's built-in "hover" function except that
* instead of firing the onMouseOver event immediately, hoverIntent checks * instead of firing the onMouseOver event immediately, hoverIntent checks
* to see if the user's mouse has slowed down (beneath the sensitivity * to see if the user's mouse has slowed down (beneath the sensitivity
* threshold) before firing the onMouseOver event. * threshold) before firing the onMouseOver event.
* *
* hoverIntent r6 // 2011.02.26 // jQuery 1.5.1+ * hoverIntent r6 // 2011.02.26 // jQuery 1.5.1+
* <http://cherne.net/brian/resources/jquery.hoverIntent.html> * <http://cherne.net/brian/resources/jquery.hoverIntent.html>
* *
* hoverIntent is currently available for use in all personal or commercial * hoverIntent is currently available for use in all personal or commercial
* projects under both MIT and GPL licenses. This means that you can choose * projects under both MIT and GPL licenses. This means that you can choose
* the license that best suits your project, and use it accordingly. * the license that best suits your project, and use it accordingly.
* *
* // basic usage (just like .hover) receives onMouseOver and onMouseOut functions * // basic usage (just like .hover) receives onMouseOver and onMouseOut functions
* $("ul li").hoverIntent( showNav , hideNav ); * $("ul li").hoverIntent( showNav , hideNav );
* *
* // advanced usage receives configuration object only * // advanced usage receives configuration object only
* $("ul li").hoverIntent({ * $("ul li").hoverIntent({
* sensitivity: 7, // number = sensitivity threshold (must be 1 or higher) * sensitivity: 7, // number = sensitivity threshold (must be 1 or higher)
* interval: 100, // number = milliseconds of polling interval * interval: 100, // number = milliseconds of polling interval
* over: showNav, // function = onMouseOver callback (required) * over: showNav, // function = onMouseOver callback (required)
* timeout: 0, // number = milliseconds delay before onMouseOut function call * timeout: 0, // number = milliseconds delay before onMouseOut function call
* out: hideNav // function = onMouseOut callback (required) * out: hideNav // function = onMouseOut callback (required)
* }); * });
* *
* @param f onMouseOver function || An object with configuration options * @param f onMouseOver function || An object with configuration options
* @param g onMouseOut function || Nothing (use configuration options object) * @param g onMouseOut function || Nothing (use configuration options object)
* @author Brian Cherne brian(at)cherne(dot)net * @author Brian Cherne brian(at)cherne(dot)net
*/ */
(function($) { (function ($) {
$.fn.hoverIntent = function(f,g) { $.fn.hoverIntent = function (f, g) {
// default configuration options // default configuration options
var cfg = { var cfg = {
sensitivity: 7, sensitivity: 7,
interval: 100, interval: 100,
timeout: 0 timeout: 0,
}; };
// override configuration options with user supplied object // override configuration options with user supplied object
cfg = $.extend(cfg, g ? { over: f, out: g } : f ); cfg = $.extend(cfg, g ? { over: f, out: g } : f);
// instantiate variables // instantiate variables
// cX, cY = current X and Y position of mouse, updated by mousemove event // cX, cY = current X and Y position of mouse, updated by mousemove event
// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
var cX, cY, pX, pY; var cX, cY, pX, pY;
// A private function for getting mouse position // A private function for getting mouse position
var track = function(ev) { var track = function (ev) {
cX = ev.pageX; cX = ev.pageX;
cY = ev.pageY; cY = ev.pageY;
}; };
// A private function for comparing current and previous mouse position // A private function for comparing current and previous mouse position
var compare = function(ev,ob) { var compare = function (ev, ob) {
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
// compare mouse positions to see if they've crossed the threshold // compare mouse positions to see if they've crossed the threshold
if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) { if (Math.abs(pX - cX) + Math.abs(pY - cY) < cfg.sensitivity) {
$(ob).off("mousemove",track); $(ob).off('mousemove', track);
// set hoverIntent state to true (so mouseOut can be called) // set hoverIntent state to true (so mouseOut can be called)
ob.hoverIntent_s = 1; ob.hoverIntent_s = 1;
return cfg.over.apply(ob,[ev]); return cfg.over.apply(ob, [ev]);
} else { } else {
// set previous coordinates for next time // set previous coordinates for next time
pX = cX; pY = cY; pX = cX;
// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs) pY = cY;
ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval ); // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
} ob.hoverIntent_t = setTimeout(function () {
}; compare(ev, ob);
}, cfg.interval);
}
};
// A private function for delaying the mouseOut function // A private function for delaying the mouseOut function
var delay = function(ev,ob) { var delay = function (ev, ob) {
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
ob.hoverIntent_s = 0; ob.hoverIntent_s = 0;
return cfg.out.apply(ob,[ev]); return cfg.out.apply(ob, [ev]);
}; };
// A private function for handling mouse 'hovering' // A private function for handling mouse 'hovering'
var handleHover = function(e) { var handleHover = function (e) {
// copy objects to be passed into t (required for event object to be passed in IE) // copy objects to be passed into t (required for event object to be passed in IE)
var ev = jQuery.extend({},e); var ev = jQuery.extend({}, e);
var ob = this; var ob = this;
// cancel hoverIntent timer if it exists // cancel hoverIntent timer if it exists
if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); } if (ob.hoverIntent_t) {
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
}
// if e.type == "mouseenter" // if e.type == "mouseenter"
if (e.type == "mouseenter") { if (e.type == 'mouseenter') {
// set "previous" X and Y position based on initial entry point // set "previous" X and Y position based on initial entry point
pX = ev.pageX; pY = ev.pageY; pX = ev.pageX;
// update "current" X and Y position based on mousemove pY = ev.pageY;
$(ob).on("mousemove",track); // update "current" X and Y position based on mousemove
// start polling interval (self-calling timeout) to compare mouse coordinates over time $(ob).on('mousemove', track);
if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );} // start polling interval (self-calling timeout) to compare mouse coordinates over time
if (ob.hoverIntent_s != 1) {
ob.hoverIntent_t = setTimeout(function () {
compare(ev, ob);
}, cfg.interval);
}
// else e.type == "mouseleave" // else e.type == "mouseleave"
} else { } else {
// unbind expensive mousemove event // unbind expensive mousemove event
$(ob).off("mousemove",track); $(ob).off('mousemove', track);
// if hoverIntent state is true, then call the mouseOut function after the specified delay // if hoverIntent state is true, then call the mouseOut function after the specified delay
if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );} if (ob.hoverIntent_s == 1) {
} ob.hoverIntent_t = setTimeout(function () {
}; delay(ev, ob);
}, cfg.timeout);
}
}
};
// bind the function to the two event listeners // bind the function to the two event listeners
return this.on('mouseenter', handleHover).on('mouseleave', handleHover); return this.on('mouseenter', handleHover).on('mouseleave', handleHover);
}; };
})(jQuery); })(jQuery);

Просмотреть файл

@ -1,111 +1,116 @@
/* jQuery Zoombox: https://github.com/technicolorenvy/jquery-zoombox */ /* jQuery Zoombox: https://github.com/technicolorenvy/jquery-zoombox */
(function($) { (function ($) {
$.fn.zoomBox = function(opts) { $.fn.zoomBox = function (opts) {
var box, boxX, boxY, boxW, boxH; var box, boxX, boxY, boxW, boxH;
var img, imgW, imgH; var img, imgW, imgH;
opts = $.extend({ opts = $.extend(
interval: 400, {
sensitivity: 100000, interval: 400,
zoomSpeed: 200, sensitivity: 100000,
zoomMargin: 10 zoomSpeed: 200,
}, opts || { }); zoomMargin: 10,
},
opts || {},
);
this.each(function() { this.each(function () {
initMetrics(this); initMetrics(this);
$(img).css( imageCenter() ); $(img).css(imageCenter());
}); });
return this.hoverIntent( $.extend({ return this.hoverIntent(
over: onMouseEnter, $.extend(
out: onMouseLeave {
}, opts)); over: onMouseEnter,
out: onMouseLeave,
},
opts,
),
);
function initMetrics(elem) {
var jimg = $('img', elem);
jimg.data('origWidth', jimg.attr('width'));
jimg.data('origHeight', jimg.attr('height'));
updateMetrics(elem);
}
function initMetrics(elem) function updateMetrics(elem) {
{ var jbox = $(elem);
var jimg = $('img', elem); var jimg = $('img', elem);
jimg.data('origWidth', jimg.attr('width'));
jimg.data('origHeight', jimg.attr('height'));
updateMetrics(elem);
}
function updateMetrics(elem) box = elem;
{ boxX = jbox.offset().left;
var jbox = $(elem); boxY = jbox.offset().top;
var jimg = $('img', elem); boxW = jbox.width();
boxH = jbox.height();
box = elem; img = jimg.get(0);
boxX = jbox.offset().left; imgW = jimg.data('origWidth');
boxY = jbox.offset().top; imgH = jimg.data('origHeight');
boxW = jbox.width(); }
boxH = jbox.height();
img = jimg.get(0); function onMouseEnter(e) {
imgW = jimg.data('origWidth'); updateMetrics(e.currentTarget);
imgH = jimg.data('origHeight');
}
function onMouseEnter(e) $(img).stop(false, true);
{
updateMetrics(e.currentTarget);
$(img).stop(false, true); $(img).animate(
imageZoom(e.pageX, e.pageY),
opts.zoomSpeed,
'linear',
function () {
$(box).on('mousemove', onMouseMove);
},
);
}
$(img).animate(imageZoom(e.pageX, e.pageY), opts.zoomSpeed, 'linear', function() { function onMouseMove(e) {
$(box).on('mousemove', onMouseMove); $(img).css(imageZoom(e.pageX, e.pageY));
}); }
}
function onMouseMove(e) function onMouseLeave(e) {
{ $(img).stop(false, true);
$(img).css( imageZoom(e.pageX, e.pageY) ); $(box).off('mousemove', onMouseMove);
} $(img).animate(imageCenter(), opts.zoomSpeed, 'linear');
box = img = null;
}
function onMouseLeave(e) function imageCenter() {
{ var sx = (boxW - 2 * opts.zoomMargin) / imgW;
$(img).stop(false, true); var sy = (boxH - 2 * opts.zoomMargin) / imgH;
$(box).off('mousemove', onMouseMove); var scale = Math.min(sx, sy);
$(img).animate(imageCenter(), opts.zoomSpeed, 'linear');
box = img = null;
}
function imageCenter() return {
{ /* line below patched to always pin images to the top right -
var sx = (boxW - 2*opts.zoomMargin) / imgW;
var sy = (boxH - 2*opts.zoomMargin) / imgH;
var scale = Math.min(sx, sy);
return {
/* line below patched to always pin images to the top right -
helps when images don't fill the div width for static themes.*/ helps when images don't fill the div width for static themes.*/
'left': (boxW - scale*imgW) - opts.zoomMargin, left: boxW - scale * imgW - opts.zoomMargin,
'top': (boxH - scale*imgH) / 2, top: (boxH - scale * imgH) / 2,
'width': scale*imgW, width: scale * imgW,
'height': scale*imgH height: scale * imgH,
}; };
} }
function imageZoom(x, y) function imageZoom(x, y) {
{ x = x - boxX;
x = x - boxX; y = y - boxY;
y = y - boxY;
var sx = boxW / imgW; var sx = boxW / imgW;
var sy = boxH / imgH; var sy = boxH / imgH;
var x2 = boxW/2 - x * imgW / boxW; var x2 = boxW / 2 - (x * imgW) / boxW;
var y2 = boxH/2 - y * imgH / boxH; var y2 = boxH / 2 - (y * imgH) / boxH;
x2 = Math.max( Math.min(x2, 0), boxW-imgW ); x2 = Math.max(Math.min(x2, 0), boxW - imgW);
y2 = Math.max( Math.min(y2, 0), boxH-imgH ); y2 = Math.max(Math.min(y2, 0), boxH - imgH);
return { return {
'left': x2, left: x2,
'top': y2, top: y2,
'width': imgW, width: imgW,
'height': imgH height: imgH,
}; };
} }
}; };
})(jQuery); })(jQuery);

Просмотреть файл

@ -2,17 +2,23 @@
// to the provided string if a translation is missing. // to the provided string if a translation is missing.
// TODO: Remove this once we upgrade to a version of Django that contains // TODO: Remove this once we upgrade to a version of Django that contains
// the fix for https://code.djangoproject.com/ticket/27418 // the fix for https://code.djangoproject.com/ticket/27418
if (window.ngettext && django && django.ngettext && django.catalog && django.pluralidx) { if (
window.ngettext &&
django &&
django.ngettext &&
django.catalog &&
django.pluralidx
) {
django.ngettext = function (singular, plural, count) { django.ngettext = function (singular, plural, count) {
var value = django.catalog[singular]; var value = django.catalog[singular];
var translation; var translation;
if (typeof(value) !== 'undefined') { if (typeof value !== 'undefined') {
translation = value[django.pluralidx(count)]; translation = value[django.pluralidx(count)];
if (translation && translation.length > 0) { if (translation && translation.length > 0) {
return translation; return translation;
} }
} }
return (count == 1) ? singular : plural; return count == 1 ? singular : plural;
}; };
window.ngettext = django.ngettext; window.ngettext = django.ngettext;
} }

Просмотреть файл

@ -1,3 +1,3 @@
; /* This is just to fix jquery.pjax.js breaking our hokey concatenation and /* This is just to fix jquery.pjax.js breaking our hokey concatenation and
* minification process. * minification process.
*/ */

139
static/js/lib/syntaxhighlighter/shBrushCss.js поставляемый
Просмотреть файл

@ -14,78 +14,85 @@
* @license * @license
* Dual licensed under the MIT and GPL licenses. * Dual licensed under the MIT and GPL licenses.
*/ */
;(function() (function () {
{ // CommonJS
// CommonJS SyntaxHighlighter =
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null); SyntaxHighlighter ||
(typeof require !== 'undefined'
? require('shCore').SyntaxHighlighter
: null);
function Brush() function Brush() {
{ function getKeywordsCSS(str) {
function getKeywordsCSS(str) return (
{ '\\b([a-z_]|)' +
return '\\b([a-z_]|)' + str.replace(/ /g, '(?=:)\\b|\\b([a-z_\\*]|\\*|)') + '(?=:)\\b'; str.replace(/ /g, '(?=:)\\b|\\b([a-z_\\*]|\\*|)') +
}; '(?=:)\\b'
);
function getValuesCSS(str) }
{
return '\\b' + str.replace(/ /g, '(?!-)(?!:)\\b|\\b()') + '\:\\b';
};
var keywords = 'ascent azimuth background-attachment background-color background-image background-position ' + function getValuesCSS(str) {
'background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top ' + return '\\b' + str.replace(/ /g, '(?!-)(?!:)\\b|\\b()') + ':\\b';
'border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color ' + }
'border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width ' +
'border-bottom-width border-left-width border-width border bottom cap-height caption-side centerline clear clip color ' +
'content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display ' +
'elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font ' +
'height left letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top ' +
'margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans ' +
'outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page ' +
'page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position ' +
'quotes right richness size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress ' +
'table-layout text-align top text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em ' +
'vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index';
var values = 'above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder '+ var keywords =
'both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed '+ 'ascent azimuth background-attachment background-color background-image background-position ' +
'continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double '+ 'background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top ' +
'embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia '+ 'border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color ' +
'gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic '+ 'border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width ' +
'justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha '+ 'border-bottom-width border-left-width border-width border bottom cap-height caption-side centerline clear clip color ' +
'lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower '+ 'content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display ' +
'navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset '+ 'elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font ' +
'outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side '+ 'height left letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top ' +
'rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow '+ 'margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans ' +
'small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize '+ 'outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page ' +
'table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal '+ 'page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position ' +
'text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin '+ 'quotes right richness size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress ' +
'upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow'; 'table-layout text-align top text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em ' +
'vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index';
var fonts = '[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif'; var values =
'above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder ' +
this.regexList = [ 'both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed ' +
{ regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' }, // multiline comments 'continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double ' +
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // double quoted strings 'embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia ' +
{ regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // single quoted strings 'gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic ' +
{ regex: /\#[a-fA-F0-9]{3,6}/g, css: 'value' }, // html colors 'justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha ' +
{ regex: /(-?\d+)(\.\d+)?(px|em|pt|\:|\%|)/g, css: 'value' }, // sizes 'lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower ' +
{ regex: /!important/g, css: 'color3' }, // !important 'navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset ' +
{ regex: new RegExp(getKeywordsCSS(keywords), 'gm'), css: 'keyword' }, // keywords 'outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side ' +
{ regex: new RegExp(getValuesCSS(values), 'g'), css: 'value' }, // values 'rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow ' +
{ regex: new RegExp(this.getKeywords(fonts), 'g'), css: 'color1' } // fonts 'small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize ' +
]; 'table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal ' +
'text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin ' +
'upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow';
this.forHtmlScript({ var fonts =
left: /(&lt;|<)\s*style.*?(&gt;|>)/gi, '[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif';
right: /(&lt;|<)\/\s*style\s*(&gt;|>)/gi
});
};
Brush.prototype = new SyntaxHighlighter.Highlighter(); this.regexList = [
Brush.aliases = ['css']; { regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' }, // multiline comments
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // double quoted strings
{ regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // single quoted strings
{ regex: /\#[a-fA-F0-9]{3,6}/g, css: 'value' }, // html colors
{ regex: /(-?\d+)(\.\d+)?(px|em|pt|\:|\%|)/g, css: 'value' }, // sizes
{ regex: /!important/g, css: 'color3' }, // !important
{ regex: new RegExp(getKeywordsCSS(keywords), 'gm'), css: 'keyword' }, // keywords
{ regex: new RegExp(getValuesCSS(values), 'g'), css: 'value' }, // values
{ regex: new RegExp(this.getKeywords(fonts), 'g'), css: 'color1' }, // fonts
];
SyntaxHighlighter.brushes.CSS = Brush; this.forHtmlScript({
left: /(&lt;|<)\s*style.*?(&gt;|>)/gi,
right: /(&lt;|<)\/\s*style\s*(&gt;|>)/gi,
});
}
// CommonJS Brush.prototype = new SyntaxHighlighter.Highlighter();
typeof(exports) != 'undefined' ? exports.Brush = Brush : null; Brush.aliases = ['css'];
SyntaxHighlighter.brushes.CSS = Brush;
// CommonJS
typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
})(); })();

Просмотреть файл

@ -14,39 +14,42 @@
* @license * @license
* Dual licensed under the MIT and GPL licenses. * Dual licensed under the MIT and GPL licenses.
*/ */
;(function() (function () {
{ // CommonJS
// CommonJS SyntaxHighlighter =
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null); SyntaxHighlighter ||
(typeof require !== 'undefined'
? require('shCore').SyntaxHighlighter
: null);
function Brush() function Brush() {
{ var keywords =
var keywords = 'break case catch class continue ' + 'break case catch class continue ' +
'default delete do else enum export extends false ' + 'default delete do else enum export extends false ' +
'for function if implements import in instanceof ' + 'for function if implements import in instanceof ' +
'interface let new null package private protected ' + 'interface let new null package private protected ' +
'static return super switch ' + 'static return super switch ' +
'this throw true try typeof var while with yield'; 'this throw true try typeof var while with yield';
var r = SyntaxHighlighter.regexLib; var r = SyntaxHighlighter.regexLib;
this.regexList = [
{ regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
{ regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
{ regex: r.singleLineCComments, css: 'comments' }, // one line comments
{ regex: r.multiLineCComments, css: 'comments' }, // multiline comments
{ regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // keywords
];
this.forHtmlScript(r.scriptScriptTags);
};
Brush.prototype = new SyntaxHighlighter.Highlighter(); this.regexList = [
Brush.aliases = ['js', 'jscript', 'javascript', 'json']; { regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
{ regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
{ regex: r.singleLineCComments, css: 'comments' }, // one line comments
{ regex: r.multiLineCComments, css: 'comments' }, // multiline comments
{ regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' }, // keywords
];
SyntaxHighlighter.brushes.JScript = Brush; this.forHtmlScript(r.scriptScriptTags);
}
// CommonJS Brush.prototype = new SyntaxHighlighter.Highlighter();
typeof(exports) != 'undefined' ? exports.Brush = Brush : null; Brush.aliases = ['js', 'jscript', 'javascript', 'json'];
SyntaxHighlighter.brushes.JScript = Brush;
// CommonJS
typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
})(); })();

Просмотреть файл

@ -14,44 +14,50 @@
* @license * @license
* Dual licensed under the MIT and GPL licenses. * Dual licensed under the MIT and GPL licenses.
*/ */
;(function() (function () {
{ // CommonJS
// CommonJS SyntaxHighlighter =
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null); SyntaxHighlighter ||
(typeof require !== 'undefined'
? require('shCore').SyntaxHighlighter
: null);
function Brush() function Brush() {
{ var keywords =
var keywords = 'abstract assert boolean break byte case catch char class const ' + 'abstract assert boolean break byte case catch char class const ' +
'continue default do double else enum extends ' + 'continue default do double else enum extends ' +
'false final finally float for goto if implements import ' + 'false final finally float for goto if implements import ' +
'instanceof int interface long native new null ' + 'instanceof int interface long native new null ' +
'package private protected public return ' + 'package private protected public return ' +
'short static strictfp super switch synchronized this throw throws true ' + 'short static strictfp super switch synchronized this throw throws true ' +
'transient try void volatile while'; 'transient try void volatile while';
this.regexList = [ this.regexList = [
{ regex: SyntaxHighlighter.regexLib.singleLineCComments, css: 'comments' }, // one line comments {
{ regex: /\/\*([^\*][\s\S]*?)?\*\//gm, css: 'comments' }, // multiline comments regex: SyntaxHighlighter.regexLib.singleLineCComments,
{ regex: /\/\*(?!\*\/)\*[\s\S]*?\*\//gm, css: 'preprocessor' }, // documentation comments css: 'comments',
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // strings }, // one line comments
{ regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // strings { regex: /\/\*([^\*][\s\S]*?)?\*\//gm, css: 'comments' }, // multiline comments
{ regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi, css: 'value' }, // numbers { regex: /\/\*(?!\*\/)\*[\s\S]*?\*\//gm, css: 'preprocessor' }, // documentation comments
{ regex: /(?!\@interface\b)\@[\$\w]+\b/g, css: 'color1' }, // annotation @anno { regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // strings
{ regex: /\@interface\b/g, css: 'color2' }, // @interface keyword { regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // strings
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // java keyword { regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi, css: 'value' }, // numbers
]; { regex: /(?!\@interface\b)\@[\$\w]+\b/g, css: 'color1' }, // annotation @anno
{ regex: /\@interface\b/g, css: 'color2' }, // @interface keyword
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' }, // java keyword
];
this.forHtmlScript({ this.forHtmlScript({
left : /(&lt;|<)%[@!=]?/g, left: /(&lt;|<)%[@!=]?/g,
right : /%(&gt;|>)/g right: /%(&gt;|>)/g,
}); });
}; }
Brush.prototype = new SyntaxHighlighter.Highlighter(); Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['java']; Brush.aliases = ['java'];
SyntaxHighlighter.brushes.Java = Brush; SyntaxHighlighter.brushes.Java = Brush;
// CommonJS // CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null; typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
})(); })();

Просмотреть файл

@ -14,20 +14,21 @@
* @license * @license
* Dual licensed under the MIT and GPL licenses. * Dual licensed under the MIT and GPL licenses.
*/ */
;(function() (function () {
{ // CommonJS
// CommonJS SyntaxHighlighter =
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null); SyntaxHighlighter ||
(typeof require !== 'undefined'
? require('shCore').SyntaxHighlighter
: null);
function Brush() function Brush() {}
{
};
Brush.prototype = new SyntaxHighlighter.Highlighter(); Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['text', 'plain']; Brush.aliases = ['text', 'plain'];
SyntaxHighlighter.brushes.Plain = Brush; SyntaxHighlighter.brushes.Plain = Brush;
// CommonJS // CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null; typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
})(); })();

124
static/js/lib/syntaxhighlighter/shBrushXml.js поставляемый
Просмотреть файл

@ -14,58 +14,90 @@
* @license * @license
* Dual licensed under the MIT and GPL licenses. * Dual licensed under the MIT and GPL licenses.
*/ */
;(function() (function () {
{ // CommonJS
// CommonJS SyntaxHighlighter =
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null); SyntaxHighlighter ||
(typeof require !== 'undefined'
? require('shCore').SyntaxHighlighter
: null);
function Brush() function Brush() {
{ function process(match, regexInfo) {
function process(match, regexInfo) var constructor = SyntaxHighlighter.Match,
{ code = match[0],
var constructor = SyntaxHighlighter.Match, tag = XRegExp.exec(
code = match[0], code,
tag = XRegExp.exec(code, XRegExp('(&lt;|<)[\\s\\/\\?!]*(?<name>[:\\w-\\.]+)', 'xg')), XRegExp('(&lt;|<)[\\s\\/\\?!]*(?<name>[:\\w-\\.]+)', 'xg'),
result = [] ),
; result = [];
if (match.attributes != null) {
var attributes,
pos = 0,
regex = XRegExp(
'(?<name> [\\w:.-]+)' +
'\\s*=\\s*' +
'(?<value> ".*?"|\'.*?\'|\\w+)',
'xg',
);
if (match.attributes != null) while ((attributes = XRegExp.exec(code, regex, pos)) != null) {
{ result.push(
var attributes, new constructor(
pos = 0, attributes.name,
regex = XRegExp('(?<name> [\\w:.-]+)' + match.index + attributes.index,
'\\s*=\\s*' + 'color1',
'(?<value> ".*?"|\'.*?\'|\\w+)', ),
'xg'); );
result.push(
new constructor(
attributes.value,
match.index +
attributes.index +
attributes[0].indexOf(attributes.value),
'string',
),
);
pos = attributes.index + attributes[0].length;
}
}
while ((attributes = XRegExp.exec(code, regex, pos)) != null) if (tag != null)
{ result.push(
result.push(new constructor(attributes.name, match.index + attributes.index, 'color1')); new constructor(
result.push(new constructor(attributes.value, match.index + attributes.index + attributes[0].indexOf(attributes.value), 'string')); tag.name,
pos = attributes.index + attributes[0].length; match.index + tag[0].indexOf(tag.name),
} 'keyword',
} ),
);
if (tag != null) return result;
result.push( }
new constructor(tag.name, match.index + tag[0].indexOf(tag.name), 'keyword')
);
return result; this.regexList = [
} {
regex: XRegExp(
'(\\&lt;|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\&gt;|>)',
'gm',
),
css: 'color2',
}, // <![ ... [ ... ]]>
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
{
regex: XRegExp(
'(&lt;|<)[\\s\\/\\?!]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(&gt;|>)',
'sg',
),
func: process,
},
];
}
this.regexList = [ Brush.prototype = new SyntaxHighlighter.Highlighter();
{ regex: XRegExp('(\\&lt;|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\&gt;|>)', 'gm'), css: 'color2' }, // <![ ... [ ... ]]> Brush.aliases = ['xml', 'xhtml', 'xslt', 'html', 'plist'];
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
{ regex: XRegExp('(&lt;|<)[\\s\\/\\?!]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(&gt;|>)', 'sg'), func: process }
];
};
Brush.prototype = new SyntaxHighlighter.Highlighter(); SyntaxHighlighter.brushes.Xml = Brush;
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html', 'plist'];
SyntaxHighlighter.brushes.Xml = Brush; // CommonJS
typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
// CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})(); })();

5715
static/js/lib/syntaxhighlighter/shCore.js поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

236
static/js/lib/syntaxhighlighter/shLegacy.js поставляемый
Просмотреть файл

@ -15,143 +15,121 @@
* Dual licensed under the MIT and GPL licenses. * Dual licensed under the MIT and GPL licenses.
*/ */
var dp = { var dp = {
SyntaxHighlighter : {} SyntaxHighlighter: {},
}; };
dp.SyntaxHighlighter = { dp.SyntaxHighlighter = {
parseParams: function( parseParams: function (
input, input,
showGutter, showGutter,
showControls, showControls,
collapseAll, collapseAll,
firstLine, firstLine,
showColumns showColumns,
) ) {
{ function getValue(list, name) {
function getValue(list, name) var regex = XRegExp('^' + name + '\\[(?<value>\\w+)\\]$', 'gi'),
{ match = null;
var regex = XRegExp('^' + name + '\\[(?<value>\\w+)\\]$', 'gi'), for (var i = 0; i < list.length; i++)
match = null if ((match = XRegExp.exec(list[i], regex)) != null) return match.value;
;
for (var i = 0; i < list.length; i++)
if ((match = XRegExp.exec(list[i], regex)) != null)
return match.value;
return null;
};
function defaultValue(value, def)
{
return value != null ? value : def;
};
function asString(value)
{
return value != null ? value.toString() : null;
};
var parts = input.split(':'), return null;
brushName = parts[0], }
options = {},
straight = { 'true' : true }
reverse = { 'true' : false },
result = null,
defaults = SyntaxHighlighter.defaults
;
for (var i in parts)
options[parts[i]] = 'true';
showGutter = asString(defaultValue(showGutter, defaults.gutter)); function defaultValue(value, def) {
showControls = asString(defaultValue(showControls, defaults.toolbar)); return value != null ? value : def;
collapseAll = asString(defaultValue(collapseAll, defaults.collapse)); }
showColumns = asString(defaultValue(showColumns, defaults.ruler));
firstLine = asString(defaultValue(firstLine, defaults['first-line']));
return { function asString(value) {
brush : brushName, return value != null ? value.toString() : null;
gutter : defaultValue(reverse[options.nogutter], showGutter), }
toolbar : defaultValue(reverse[options.nocontrols], showControls),
collapse : defaultValue(straight[options.collapse], collapseAll),
// ruler : defaultValue(straight[options.showcolumns], showColumns),
'first-line' : defaultValue(getValue(parts, 'firstline'), firstLine)
};
},
HighlightAll: function(
name,
showGutter /* optional */,
showControls /* optional */,
collapseAll /* optional */,
firstLine /* optional */,
showColumns /* optional */
)
{
function findValue()
{
var a = arguments;
for (var i = 0; i < a.length; i++)
{
if (a[i] === null)
continue;
if (typeof(a[i]) == 'string' && a[i] != '')
return a[i] + '';
if (typeof(a[i]) == 'object' && a[i].value != '')
return a[i].value + '';
}
return null;
};
function findTagsByName(list, name, tagName) var parts = input.split(':'),
{ brushName = parts[0],
var tags = document.getElementsByTagName(tagName); options = {},
straight = { true: true };
for (var i = 0; i < tags.length; i++) (reverse = { true: false }),
if (tags[i].getAttribute('name') == name) (result = null),
list.push(tags[i]); (defaults = SyntaxHighlighter.defaults);
}
var elements = [],
highlighter = null,
registered = {},
propertyName = 'innerHTML'
;
// for some reason IE doesn't find <pre/> by name, however it does see them just fine by tag name...
findTagsByName(elements, name, 'pre');
findTagsByName(elements, name, 'textarea');
if (elements.length === 0) for (var i in parts) options[parts[i]] = 'true';
return;
for (var i = 0; i < elements.length; i++)
{
var element = elements[i],
params = findValue(
element.attributes['class'], element.className,
element.attributes['language'], element.language
),
language = ''
;
if (params === null)
continue;
params = dp.SyntaxHighlighter.parseParams( showGutter = asString(defaultValue(showGutter, defaults.gutter));
params, showControls = asString(defaultValue(showControls, defaults.toolbar));
showGutter, collapseAll = asString(defaultValue(collapseAll, defaults.collapse));
showControls, showColumns = asString(defaultValue(showColumns, defaults.ruler));
collapseAll, firstLine = asString(defaultValue(firstLine, defaults['first-line']));
firstLine,
showColumns
);
SyntaxHighlighter.highlight(params, element); return {
} brush: brushName,
} gutter: defaultValue(reverse[options.nogutter], showGutter),
toolbar: defaultValue(reverse[options.nocontrols], showControls),
collapse: defaultValue(straight[options.collapse], collapseAll),
// ruler : defaultValue(straight[options.showcolumns], showColumns),
'first-line': defaultValue(getValue(parts, 'firstline'), firstLine),
};
},
HighlightAll: function (
name,
showGutter /* optional */,
showControls /* optional */,
collapseAll /* optional */,
firstLine /* optional */,
showColumns /* optional */,
) {
function findValue() {
var a = arguments;
for (var i = 0; i < a.length; i++) {
if (a[i] === null) continue;
if (typeof a[i] == 'string' && a[i] != '') return a[i] + '';
if (typeof a[i] == 'object' && a[i].value != '') return a[i].value + '';
}
return null;
}
function findTagsByName(list, name, tagName) {
var tags = document.getElementsByTagName(tagName);
for (var i = 0; i < tags.length; i++)
if (tags[i].getAttribute('name') == name) list.push(tags[i]);
}
var elements = [],
highlighter = null,
registered = {},
propertyName = 'innerHTML';
// for some reason IE doesn't find <pre/> by name, however it does see them just fine by tag name...
findTagsByName(elements, name, 'pre');
findTagsByName(elements, name, 'textarea');
if (elements.length === 0) return;
for (var i = 0; i < elements.length; i++) {
var element = elements[i],
params = findValue(
element.attributes['class'],
element.className,
element.attributes['language'],
element.language,
),
language = '';
if (params === null) continue;
params = dp.SyntaxHighlighter.parseParams(
params,
showGutter,
showControls,
collapseAll,
firstLine,
showColumns,
);
SyntaxHighlighter.highlight(params, element);
}
},
}; };

Просмотреть файл

@ -1,72 +1,79 @@
var truncate = (function() { var truncate = (function () {
function text(node, trim) { function text(node, trim) {
var cn = node.childNodes; var cn = node.childNodes;
var t=''; var t = '';
if (cn.length) { if (cn.length) {
for (var i=0; i<cn.length; i++) { for (var i = 0; i < cn.length; i++) {
t += text(cn[i]); t += text(cn[i]);
} }
} else {
t = _.escape(node.textContent);
}
if (trim) {
return t.replace(/^\s+|\s+$/g, '');
}
return t;
}
function truncate(el, opts) {
opts = opts || {};
if (!opts.dir || opts.dir != 'v') return this;
var showTitle = opts.showTitle || false;
var dir = (opts.dir && opts.dir[0]) || 'h';
var scrollProp = dir == 'h' ? 'scrollWidth' : 'scrollHeight';
var offsetProp = dir == 'h' ? 'offsetWidth' : 'offsetHeight';
var truncText = opts.truncText || '&hellip;';
var trim = opts.trim || false;
var textEl = opts.textEl || el;
var split = [' ', ''],
counter,
success;
var txt, cutoff, delim;
var oldtext = textEl.getAttribute('data-oldtext') || text(textEl, trim);
textEl.setAttribute('data-oldtext', oldtext);
for (var i = 0; i < split.length; i++) {
delim = split[i];
txt = oldtext.split(delim);
cutoff = txt.length;
success = false;
if (textEl.getAttribute('data-oldtext')) {
textEl.innerHTML = oldtext;
}
if (el[scrollProp] - el[offsetProp] < 1) {
el.removeAttribute('data-truncated', null);
break;
}
var chunk = Math.ceil(txt.length / 2),
oc = 0,
wid;
for (counter = 0; counter < 15; counter++) {
textEl.innerHTML = txt.slice(0, cutoff).join(delim) + truncText;
wid = el[scrollProp] - el[offsetProp];
if (cutoff < 1) {
break;
} else if (wid < 2 && chunk == oc) {
if (
dir === 'h' ||
(delim === '' && el.scrollWidth < el.offsetWidth)
) {
success = true;
el.setAttribute('data-truncated', true);
break;
}
} else if (wid > 1) {
cutoff -= chunk;
} else { } else {
t = _.escape(node.textContent); cutoff += chunk;
} }
if (trim) { oc = chunk;
return t.replace(/^\s+|\s+$/g, ""); chunk = Math.ceil(chunk / 2);
} }
return t; if (success) break;
} }
if (showTitle && oldtext != text(textEl, trim)) {
function truncate(el, opts) { textEl.setAttribute('title', oldtext);
opts = opts || {};
if (!opts.dir || opts.dir != 'v') return this;
var showTitle = opts.showTitle || false;
var dir = (opts.dir && opts.dir[0]) || 'h';
var scrollProp = dir == "h" ? "scrollWidth" : "scrollHeight";
var offsetProp = dir == "h" ? "offsetWidth" : "offsetHeight";
var truncText = opts.truncText || "&hellip;";
var trim = opts.trim || false;
var textEl = opts.textEl || el;
var split = [" ",""], counter, success;
var txt, cutoff, delim;
var oldtext = textEl.getAttribute("data-oldtext") || text(textEl, trim);
textEl.setAttribute("data-oldtext", oldtext);
for (var i=0; i<split.length; i++) {
delim = split[i];
txt = oldtext.split(delim);
cutoff = txt.length;
success = false;
if (textEl.getAttribute("data-oldtext")) {
textEl.innerHTML = oldtext;
}
if ((el[scrollProp] - el[offsetProp]) < 1) {
el.removeAttribute("data-truncated", null);
break;
}
var chunk = Math.ceil(txt.length/2), oc=0, wid;
for (counter = 0; counter < 15; counter++) {
textEl.innerHTML = txt.slice(0,cutoff).join(delim) + truncText;
wid = (el[scrollProp] - el[offsetProp]);
if (cutoff < 1) {
break;
} else if (wid < 2 && chunk == oc) {
if (dir === 'h' || (delim === '' && el.scrollWidth < el.offsetWidth)) {
success = true;
el.setAttribute("data-truncated", true);
break;
}
} else if (wid > 1) {
cutoff -= chunk;
} else {
cutoff += chunk;
}
oc = chunk;
chunk = Math.ceil(chunk/2);
}
if (success) break;
}
if (showTitle && oldtext != text(textEl, trim)) {
textEl.setAttribute("title", oldtext);
}
} }
}
return truncate; return truncate;
})(); })();

Просмотреть файл

@ -13,79 +13,95 @@
*/ */
// Offering a Custom Alias suport - More info: http://docs.jquery.com/Plugins/Authoring#Custom_Alias // Offering a Custom Alias suport - More info: http://docs.jquery.com/Plugins/Authoring#Custom_Alias
(function($) { (function ($) {
/**
* $ is an alias to jQuery object
*
*/
$.fn.lightBox = function (settings) {
// Settings to configure the jQuery lightBox plugin how you like
settings = jQuery.extend(
{
// Configuration related to overlay
overlayBgColor: '#000', // (string) Background color to overlay; inform a hexadecimal value like: #RRGGBB. Where RR, GG, and BB are the hexadecimal values for the red, green, and blue values of the color.
overlayOpacity: 0.8, // (integer) Opacity value to overlay; inform: 0.X. Where X are number from 0 to 9
// Configuration related to images
imageLoading: 'images/lightbox-ico-loading.gif', // (string) Path and the name of the loading icon
imageBtnPrev: 'images/lightbox-btn-prev.gif', // (string) Path and the name of the prev button image
imageBtnNext: 'images/lightbox-btn-next.gif', // (string) Path and the name of the next button image
imageBtnClose: 'images/lightbox-btn-close.gif', // (string) Path and the name of the close btn
imageBlank: 'images/lightbox-blank.gif', // (string) Path and the name of a blank image (one pixel)
// Configuration related to container image box
containerBorderSize: 10, // (integer) If you adjust the padding in the CSS for the container, #lightbox-container-image-box, you will need to update this value
containerResizeSpeed: 400, // (integer) Specify the resize duration of container image. These number are miliseconds. 400 is default.
// Configuration related to texts in caption. For example: Image 2 of 8. You can alter either "Image" and "of" texts.
txtImage: 'Image', // (string) Specify text "Image"
txtOf: 'of', // (string) Specify text "of"
// Configuration related to keyboard navigation
keyToClose: 'c', // (string) (c = close) Letter to close the jQuery lightBox interface. Beyond this letter, the letter X and the SCAPE key is used to.
keyToPrev: 'p', // (string) (p = previous) Letter to show the previous image
keyToNext: 'n', // (string) (n = next) Letter to show the next image.
// Don´t alter these variables in any way
imageArray: [],
activeImage: 0,
},
settings,
);
// Caching the jQuery object with all elements matched
var jQueryMatchedObj = this; // This, in this context, refer to jQuery object
/** /**
* $ is an alias to jQuery object * Initializing the plugin calling the start function
* *
* @return boolean false
*/ */
$.fn.lightBox = function(settings) { function _initialize() {
// Settings to configure the jQuery lightBox plugin how you like _start(this, jQueryMatchedObj); // This, in this context, refer to object (link) which the user have clicked
settings = jQuery.extend({ return false; // Avoid the browser following the link
// Configuration related to overlay }
overlayBgColor: '#000', // (string) Background color to overlay; inform a hexadecimal value like: #RRGGBB. Where RR, GG, and BB are the hexadecimal values for the red, green, and blue values of the color. /**
overlayOpacity: 0.8, // (integer) Opacity value to overlay; inform: 0.X. Where X are number from 0 to 9 * Start the jQuery lightBox plugin
// Configuration related to images *
imageLoading: 'images/lightbox-ico-loading.gif', // (string) Path and the name of the loading icon * @param object objClicked The object (link) whick the user have clicked
imageBtnPrev: 'images/lightbox-btn-prev.gif', // (string) Path and the name of the prev button image * @param object jQueryMatchedObj The jQuery object with all elements matched
imageBtnNext: 'images/lightbox-btn-next.gif', // (string) Path and the name of the next button image */
imageBtnClose: 'images/lightbox-btn-close.gif', // (string) Path and the name of the close btn function _start(objClicked, jQueryMatchedObj) {
imageBlank: 'images/lightbox-blank.gif', // (string) Path and the name of a blank image (one pixel) // Hime some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
// Configuration related to container image box $('embed, object, select').css({ visibility: 'hidden' });
containerBorderSize: 10, // (integer) If you adjust the padding in the CSS for the container, #lightbox-container-image-box, you will need to update this value // Call the function to create the markup structure; style some elements; assign events in some elements.
containerResizeSpeed: 400, // (integer) Specify the resize duration of container image. These number are miliseconds. 400 is default. _set_interface();
// Configuration related to texts in caption. For example: Image 2 of 8. You can alter either "Image" and "of" texts. // Unset total images in imageArray
txtImage: 'Image', // (string) Specify text "Image" settings.imageArray.length = 0;
txtOf: 'of', // (string) Specify text "of" // Unset image active information
// Configuration related to keyboard navigation settings.activeImage = 0;
keyToClose: 'c', // (string) (c = close) Letter to close the jQuery lightBox interface. Beyond this letter, the letter X and the SCAPE key is used to. // We have an image set? Or just an image? Let´s see it.
keyToPrev: 'p', // (string) (p = previous) Letter to show the previous image if (jQueryMatchedObj.length == 1) {
keyToNext: 'n', // (string) (n = next) Letter to show the next image. settings.imageArray.push(
// Don´t alter these variables in any way new Array(
imageArray: [], objClicked.getAttribute('href'),
activeImage: 0 objClicked.getAttribute('title'),
},settings); ),
// Caching the jQuery object with all elements matched );
var jQueryMatchedObj = this; // This, in this context, refer to jQuery object } else {
/** // Add an Array (as many as we have), with href and title atributes, inside the Array that storage the images references
* Initializing the plugin calling the start function for (var i = 0; i < jQueryMatchedObj.length; i++) {
* settings.imageArray.push(
* @return boolean false new Array(
*/ jQueryMatchedObj[i].getAttribute('href'),
function _initialize() { jQueryMatchedObj[i].getAttribute('title'),
_start(this,jQueryMatchedObj); // This, in this context, refer to object (link) which the user have clicked ),
return false; // Avoid the browser following the link );
} }
/** }
* Start the jQuery lightBox plugin while (
* settings.imageArray[settings.activeImage][0] !=
* @param object objClicked The object (link) whick the user have clicked objClicked.getAttribute('href')
* @param object jQueryMatchedObj The jQuery object with all elements matched ) {
*/ settings.activeImage++;
function _start(objClicked,jQueryMatchedObj) { }
// Hime some elements to avoid conflict with overlay in IE. These elements appear above the overlay. // Call the function that prepares image exibition
$('embed, object, select').css({ 'visibility' : 'hidden' }); _set_image_to_view();
// Call the function to create the markup structure; style some elements; assign events in some elements. }
_set_interface(); /**
// Unset total images in imageArray
settings.imageArray.length = 0;
// Unset image active information
settings.activeImage = 0;
// We have an image set? Or just an image? Let´s see it.
if ( jQueryMatchedObj.length == 1 ) {
settings.imageArray.push(new Array(objClicked.getAttribute('href'),objClicked.getAttribute('title')));
} else {
// Add an Array (as many as we have), with href and title atributes, inside the Array that storage the images references
for ( var i = 0; i < jQueryMatchedObj.length; i++ ) {
settings.imageArray.push(new Array(jQueryMatchedObj[i].getAttribute('href'),jQueryMatchedObj[i].getAttribute('title')));
}
}
while ( settings.imageArray[settings.activeImage][0] != objClicked.getAttribute('href') ) {
settings.activeImage++;
}
// Call the function that prepares image exibition
_set_image_to_view();
}
/**
* Create the jQuery lightBox plugin interface * Create the jQuery lightBox plugin interface
* *
* The HTML markup will be like that: * The HTML markup will be like that:
@ -121,326 +137,414 @@
</div> </div>
* *
*/ */
function _set_interface() { function _set_interface() {
// Apply the HTML markup into body tag // Apply the HTML markup into body tag
$('body').append('<div id="jquery-overlay"></div><div id="jquery-lightbox"><div id="lightbox-container-image-box"><div id="lightbox-container-image"><img id="lightbox-image"><div style="" id="lightbox-nav"><a href="#" id="lightbox-nav-btnPrev"></a><a href="#" id="lightbox-nav-btnNext"></a></div><div id="lightbox-loading"><a href="#" id="lightbox-loading-link"><img src="' + settings.imageLoading + '"></a></div></div></div><div id="lightbox-container-image-data-box"><div id="lightbox-container-image-data"><div id="lightbox-image-details"><span id="lightbox-image-details-caption"></span><span id="lightbox-image-details-currentNumber"></span></div><div id="lightbox-secNav"><a href="#" id="lightbox-secNav-btnClose"><span class="sr-only">' + gettext('close') + '</span></a></div></div></div></div>'); $('body').append(
// Get page sizes '<div id="jquery-overlay"></div><div id="jquery-lightbox"><div id="lightbox-container-image-box"><div id="lightbox-container-image"><img id="lightbox-image"><div style="" id="lightbox-nav"><a href="#" id="lightbox-nav-btnPrev"></a><a href="#" id="lightbox-nav-btnNext"></a></div><div id="lightbox-loading"><a href="#" id="lightbox-loading-link"><img src="' +
var arrPageSizes = ___getPageSize(); settings.imageLoading +
// Style overlay and show it '"></a></div></div></div><div id="lightbox-container-image-data-box"><div id="lightbox-container-image-data"><div id="lightbox-image-details"><span id="lightbox-image-details-caption"></span><span id="lightbox-image-details-currentNumber"></span></div><div id="lightbox-secNav"><a href="#" id="lightbox-secNav-btnClose"><span class="sr-only">' +
$('#jquery-overlay').css({ gettext('close') +
backgroundColor: settings.overlayBgColor, '</span></a></div></div></div></div>',
opacity: settings.overlayOpacity, );
width: arrPageSizes[0], // Get page sizes
height: arrPageSizes[1] var arrPageSizes = ___getPageSize();
}).fadeIn(); // Style overlay and show it
// Get page scroll $('#jquery-overlay')
var arrPageScroll = ___getPageScroll(); .css({
// Calculate top and left offset for the jquery-lightbox div object and show it backgroundColor: settings.overlayBgColor,
$('#jquery-lightbox').css({ opacity: settings.overlayOpacity,
top: arrPageScroll[1] + (arrPageSizes[3] / 10), width: arrPageSizes[0],
left: arrPageScroll[0] height: arrPageSizes[1],
}).show(); })
// Assigning click events in elements to close overlay .fadeIn();
$('#jquery-overlay,#jquery-lightbox').click(function() { // Get page scroll
_finish(); var arrPageScroll = ___getPageScroll();
}); // Calculate top and left offset for the jquery-lightbox div object and show it
// Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects $('#jquery-lightbox')
$('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function() { .css({
_finish(); top: arrPageScroll[1] + arrPageSizes[3] / 10,
return false; left: arrPageScroll[0],
}); })
// If window was resized, calculate the new overlay dimensions .show();
$(window).resize(function() { // Assigning click events in elements to close overlay
// Get page sizes $('#jquery-overlay,#jquery-lightbox').click(function () {
var arrPageSizes = ___getPageSize(); _finish();
// Style overlay and show it });
$('#jquery-overlay').css({ // Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects
width: arrPageSizes[0], $('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function () {
height: arrPageSizes[1] _finish();
}); return false;
// Get page scroll });
var arrPageScroll = ___getPageScroll(); // If window was resized, calculate the new overlay dimensions
// Calculate top and left offset for the jquery-lightbox div object and show it $(window).resize(function () {
$('#jquery-lightbox').css({ // Get page sizes
top: arrPageScroll[1] + (arrPageSizes[3] / 10), var arrPageSizes = ___getPageSize();
left: arrPageScroll[0] // Style overlay and show it
}); $('#jquery-overlay').css({
}); width: arrPageSizes[0],
height: arrPageSizes[1],
});
// Get page scroll
var arrPageScroll = ___getPageScroll();
// Calculate top and left offset for the jquery-lightbox div object and show it
$('#jquery-lightbox').css({
top: arrPageScroll[1] + arrPageSizes[3] / 10,
left: arrPageScroll[0],
});
});
}
/**
* Prepares image exibition; doing a image´s preloader to calculate it´s size
*
*/
function _set_image_to_view() {
// show the loading
// Show the loading
$('#lightbox-loading').show();
// Hide some elements
$(
'#lightbox-image,#lightbox-nav,#lightbox-nav-btnPrev,#lightbox-nav-btnNext,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber',
).hide();
// Image preload process
var objImagePreloader = new Image();
objImagePreloader.onload = function () {
$('#lightbox-image').attr(
'src',
settings.imageArray[settings.activeImage][0],
);
// Perfomance an effect in the image container resizing it
_resize_container_image_box(
objImagePreloader.width,
objImagePreloader.height,
);
// clear onLoad, IE behaves irratically with animated gifs otherwise
objImagePreloader.onload = function () {};
};
objImagePreloader.src = settings.imageArray[settings.activeImage][0];
}
/**
* Perfomance an effect in the image container resizing it
*
* @param integer intImageWidth The image´s width that will be showed
* @param integer intImageHeight The image´s height that will be showed
*/
function _resize_container_image_box(intImageWidth, intImageHeight) {
// Get current width and height
var intCurrentWidth = $('#lightbox-container-image-box').width();
var intCurrentHeight = $('#lightbox-container-image-box').height();
// Get the width and height of the selected image plus the padding
var intWidth = intImageWidth + settings.containerBorderSize * 2; // Plus the image´s width and the left and right padding value
var intHeight = intImageHeight + settings.containerBorderSize * 2; // Plus the image´s height and the left and right padding value
// Diferences
var intDiffW = intCurrentWidth - intWidth;
var intDiffH = intCurrentHeight - intHeight;
// Perfomance the effect
$('#lightbox-container-image-box').animate(
{ width: intWidth, height: intHeight },
settings.containerResizeSpeed,
function () {
_show_image();
},
);
if (intDiffW == 0 && intDiffH == 0) {
if ($.browser.msie) {
___pause(250);
} else {
___pause(100);
} }
/** }
* Prepares image exibition; doing a image´s preloader to calculate it´s size $('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({
* height: intImageHeight + settings.containerBorderSize * 2,
*/ });
function _set_image_to_view() { // show the loading $('#lightbox-container-image-data-box').css({ width: intImageWidth });
// Show the loading }
$('#lightbox-loading').show(); /**
// Hide some elements * Show the prepared image
$('#lightbox-image,#lightbox-nav,#lightbox-nav-btnPrev,#lightbox-nav-btnNext,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber').hide(); *
// Image preload process */
var objImagePreloader = new Image(); function _show_image() {
objImagePreloader.onload = function() { $('#lightbox-loading').hide();
$('#lightbox-image').attr('src',settings.imageArray[settings.activeImage][0]); $('#lightbox-image').fadeIn(function () {
// Perfomance an effect in the image container resizing it _show_image_data();
_resize_container_image_box(objImagePreloader.width,objImagePreloader.height); _set_navigation();
// clear onLoad, IE behaves irratically with animated gifs otherwise });
objImagePreloader.onload=function(){}; _preload_neighbor_images();
} }
objImagePreloader.src = settings.imageArray[settings.activeImage][0]; /**
}; * Show the image information
/** *
* Perfomance an effect in the image container resizing it */
* function _show_image_data() {
* @param integer intImageWidth The image´s width that will be showed $('#lightbox-container-image-data-box').slideDown('fast');
* @param integer intImageHeight The image´s height that will be showed $('#lightbox-image-details-caption').hide();
*/ if (settings.imageArray[settings.activeImage][1]) {
function _resize_container_image_box(intImageWidth,intImageHeight) { $('#lightbox-image-details-caption')
// Get current width and height .text(settings.imageArray[settings.activeImage][1])
var intCurrentWidth = $('#lightbox-container-image-box').width(); .show();
var intCurrentHeight = $('#lightbox-container-image-box').height(); }
// Get the width and height of the selected image plus the padding // If we have a image set, display 'Image X of X'
var intWidth = (intImageWidth + (settings.containerBorderSize * 2)); // Plus the image´s width and the left and right padding value if (settings.imageArray.length > 1) {
var intHeight = (intImageHeight + (settings.containerBorderSize * 2)); // Plus the image´s height and the left and right padding value $('#lightbox-image-details-currentNumber')
// Diferences .html(
var intDiffW = intCurrentWidth - intWidth; settings.txtImage +
var intDiffH = intCurrentHeight - intHeight; ' ' +
// Perfomance the effect (settings.activeImage + 1) +
$('#lightbox-container-image-box').animate({ width: intWidth, height: intHeight },settings.containerResizeSpeed,function() { _show_image(); }); ' ' +
if ( ( intDiffW == 0 ) && ( intDiffH == 0 ) ) { settings.txtOf +
if ( $.browser.msie ) { ' ' +
___pause(250); settings.imageArray.length,
} else { )
___pause(100); .show();
} }
} }
$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ height: intImageHeight + (settings.containerBorderSize * 2) }); /**
$('#lightbox-container-image-data-box').css({ width: intImageWidth }); * Display the button navigations
}; *
/** */
* Show the prepared image function _set_navigation() {
* $('#lightbox-nav').show();
*/
function _show_image() {
$('#lightbox-loading').hide();
$('#lightbox-image').fadeIn(function() {
_show_image_data();
_set_navigation();
});
_preload_neighbor_images();
};
/**
* Show the image information
*
*/
function _show_image_data() {
$('#lightbox-container-image-data-box').slideDown('fast');
$('#lightbox-image-details-caption').hide();
if ( settings.imageArray[settings.activeImage][1] ) {
$('#lightbox-image-details-caption').text(settings.imageArray[settings.activeImage][1]).show();
}
// If we have a image set, display 'Image X of X'
if ( settings.imageArray.length > 1 ) {
$('#lightbox-image-details-currentNumber').html(settings.txtImage + ' ' + ( settings.activeImage + 1 ) + ' ' + settings.txtOf + ' ' + settings.imageArray.length).show();
}
}
/**
* Display the button navigations
*
*/
function _set_navigation() {
$('#lightbox-nav').show();
// Instead to define this configuration in CSS file, we define here. And it´s need to IE. Just. // Instead to define this configuration in CSS file, we define here. And it´s need to IE. Just.
$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' }); $('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({
background: 'transparent url(' + settings.imageBlank + ') no-repeat',
});
// Show the prev button, if not the first image in set // Show the prev button, if not the first image in set
if ( settings.activeImage != 0 ) { if (settings.activeImage != 0) {
// Show the images button for Next buttons // Show the images button for Next buttons
$('#lightbox-nav-btnPrev').off().hover(function() { $('#lightbox-nav-btnPrev')
$(this).css({ 'background' : 'url(' + settings.imageBtnPrev + ') left 45% no-repeat' }); .off()
},function() { .hover(
$(this).css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' }); function () {
}).show().on('click', function() { $(this).css({
settings.activeImage = settings.activeImage - 1; background:
_set_image_to_view(); 'url(' + settings.imageBtnPrev + ') left 45% no-repeat',
return false; });
}); },
} function () {
$(this).css({
background:
'transparent url(' + settings.imageBlank + ') no-repeat',
});
},
)
.show()
.on('click', function () {
settings.activeImage = settings.activeImage - 1;
_set_image_to_view();
return false;
});
}
// Show the next button, if not the last image in set // Show the next button, if not the last image in set
if ( settings.activeImage != ( settings.imageArray.length -1 ) ) { if (settings.activeImage != settings.imageArray.length - 1) {
// Show the images button for Next buttons // Show the images button for Next buttons
$('#lightbox-nav-btnNext').off().hover(function() { $('#lightbox-nav-btnNext')
$(this).css({ 'background' : 'url(' + settings.imageBtnNext + ') right 45% no-repeat' }); .off()
},function() { .hover(
$(this).css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' }); function () {
}).show().on('click', function() { $(this).css({
settings.activeImage = settings.activeImage + 1; background:
_set_image_to_view(); 'url(' + settings.imageBtnNext + ') right 45% no-repeat',
return false; });
}); },
} function () {
// Enable keyboard navigation $(this).css({
_enable_keyboard_navigation(); background:
'transparent url(' + settings.imageBlank + ') no-repeat',
});
},
)
.show()
.on('click', function () {
settings.activeImage = settings.activeImage + 1;
_set_image_to_view();
return false;
});
}
// Enable keyboard navigation
_enable_keyboard_navigation();
}
/**
* Enable a support to keyboard navigation
*
*/
function _enable_keyboard_navigation() {
$(document).keydown(function (objEvent) {
_keyboard_action(objEvent);
});
}
/**
* Disable the support to keyboard navigation
*
*/
function _disable_keyboard_navigation() {
$(document).off();
}
/**
* Perform the keyboard actions
*
*/
function _keyboard_action(objEvent) {
// To ie
if (objEvent == null) {
keycode = event.keyCode;
escapeKey = 27;
// To Mozilla
} else {
keycode = objEvent.keyCode;
escapeKey = objEvent.DOM_VK_ESCAPE;
}
// Get the key in lower case form
key = String.fromCharCode(keycode).toLowerCase();
// Verify the keys to close the ligthBox
if (key == settings.keyToClose || key == 'x' || keycode == escapeKey) {
_finish();
}
// Verify the key to show the previous image
if (key == settings.keyToPrev || keycode == 37) {
// If we´re not showing the first image, call the previous
if (settings.activeImage != 0) {
settings.activeImage = settings.activeImage - 1;
_set_image_to_view();
_disable_keyboard_navigation();
} }
/** }
* Enable a support to keyboard navigation // Verify the key to show the next image
* if (key == settings.keyToNext || keycode == 39) {
*/ // If we´re not showing the last image, call the next
function _enable_keyboard_navigation() { if (settings.activeImage != settings.imageArray.length - 1) {
$(document).keydown(function(objEvent) { settings.activeImage = settings.activeImage + 1;
_keyboard_action(objEvent); _set_image_to_view();
}); _disable_keyboard_navigation();
} }
/** }
* Disable the support to keyboard navigation }
* /**
*/ * Preload prev and next images being showed
function _disable_keyboard_navigation() { *
$(document).off(); */
} function _preload_neighbor_images() {
/** if (settings.imageArray.length - 1 > settings.activeImage) {
* Perform the keyboard actions objNext = new Image();
* objNext.src = settings.imageArray[settings.activeImage + 1][0];
*/ }
function _keyboard_action(objEvent) { if (settings.activeImage > 0) {
// To ie objPrev = new Image();
if ( objEvent == null ) { objPrev.src = settings.imageArray[settings.activeImage - 1][0];
keycode = event.keyCode; }
escapeKey = 27; }
// To Mozilla /**
} else { * Remove jQuery lightBox plugin HTML markup
keycode = objEvent.keyCode; *
escapeKey = objEvent.DOM_VK_ESCAPE; */
} function _finish() {
// Get the key in lower case form $('#jquery-lightbox').remove();
key = String.fromCharCode(keycode).toLowerCase(); $('#jquery-overlay').fadeOut(function () {
// Verify the keys to close the ligthBox $('#jquery-overlay').remove();
if ( ( key == settings.keyToClose ) || ( key == 'x' ) || ( keycode == escapeKey ) ) { });
_finish(); // Show some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
} $('embed, object, select').css({ visibility: 'visible' });
// Verify the key to show the previous image }
if ( ( key == settings.keyToPrev ) || ( keycode == 37 ) ) { /**
// If we´re not showing the first image, call the previous
if ( settings.activeImage != 0 ) {
settings.activeImage = settings.activeImage - 1;
_set_image_to_view();
_disable_keyboard_navigation();
}
}
// Verify the key to show the next image
if ( ( key == settings.keyToNext ) || ( keycode == 39 ) ) {
// If we´re not showing the last image, call the next
if ( settings.activeImage != ( settings.imageArray.length - 1 ) ) {
settings.activeImage = settings.activeImage + 1;
_set_image_to_view();
_disable_keyboard_navigation();
}
}
}
/**
* Preload prev and next images being showed
*
*/
function _preload_neighbor_images() {
if ( (settings.imageArray.length -1) > settings.activeImage ) {
objNext = new Image();
objNext.src = settings.imageArray[settings.activeImage + 1][0];
}
if ( settings.activeImage > 0 ) {
objPrev = new Image();
objPrev.src = settings.imageArray[settings.activeImage -1][0];
}
}
/**
* Remove jQuery lightBox plugin HTML markup
*
*/
function _finish() {
$('#jquery-lightbox').remove();
$('#jquery-overlay').fadeOut(function() { $('#jquery-overlay').remove(); });
// Show some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
$('embed, object, select').css({ 'visibility' : 'visible' });
}
/**
/ THIRD FUNCTION / THIRD FUNCTION
* getPageSize() by quirksmode.com * getPageSize() by quirksmode.com
* *
* @return Array Return an array with page width, height and window width, height * @return Array Return an array with page width, height and window width, height
*/ */
function ___getPageSize() { function ___getPageSize() {
var xScroll, yScroll; var xScroll, yScroll;
if (window.innerHeight && window.scrollMaxY) { if (window.innerHeight && window.scrollMaxY) {
xScroll = window.innerWidth + window.scrollMaxX; xScroll = window.innerWidth + window.scrollMaxX;
yScroll = window.innerHeight + window.scrollMaxY; yScroll = window.innerHeight + window.scrollMaxY;
} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac } else if (document.body.scrollHeight > document.body.offsetHeight) {
xScroll = document.body.scrollWidth; // all but Explorer Mac
yScroll = document.body.scrollHeight; xScroll = document.body.scrollWidth;
} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari yScroll = document.body.scrollHeight;
xScroll = document.body.offsetWidth; } else {
yScroll = document.body.offsetHeight; // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
} xScroll = document.body.offsetWidth;
var windowWidth, windowHeight; yScroll = document.body.offsetHeight;
if (self.innerHeight) { // all except Explorer }
if(document.documentElement.clientWidth){ var windowWidth, windowHeight;
windowWidth = document.documentElement.clientWidth; if (self.innerHeight) {
} else { // all except Explorer
windowWidth = self.innerWidth; if (document.documentElement.clientWidth) {
} windowWidth = document.documentElement.clientWidth;
windowHeight = self.innerHeight; } else {
} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode windowWidth = self.innerWidth;
windowWidth = document.documentElement.clientWidth; }
windowHeight = document.documentElement.clientHeight; windowHeight = self.innerHeight;
} else if (document.body) { // other Explorers } else if (
windowWidth = document.body.clientWidth; document.documentElement &&
windowHeight = document.body.clientHeight; document.documentElement.clientHeight
} ) {
// for small pages with total height less then height of the viewport // Explorer 6 Strict Mode
if(yScroll < windowHeight){ windowWidth = document.documentElement.clientWidth;
pageHeight = windowHeight; windowHeight = document.documentElement.clientHeight;
} else { } else if (document.body) {
pageHeight = yScroll; // other Explorers
} windowWidth = document.body.clientWidth;
// for small pages with total width less then width of the viewport windowHeight = document.body.clientHeight;
if(xScroll < windowWidth){ }
pageWidth = xScroll; // for small pages with total height less then height of the viewport
} else { if (yScroll < windowHeight) {
pageWidth = windowWidth; pageHeight = windowHeight;
} } else {
arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight) pageHeight = yScroll;
return arrayPageSize; }
}; // for small pages with total width less then width of the viewport
/** if (xScroll < windowWidth) {
pageWidth = xScroll;
} else {
pageWidth = windowWidth;
}
arrayPageSize = new Array(
pageWidth,
pageHeight,
windowWidth,
windowHeight,
);
return arrayPageSize;
}
/**
/ THIRD FUNCTION / THIRD FUNCTION
* getPageScroll() by quirksmode.com * getPageScroll() by quirksmode.com
* *
* @return Array Return an array with x,y page scroll values. * @return Array Return an array with x,y page scroll values.
*/ */
function ___getPageScroll() { function ___getPageScroll() {
var xScroll, yScroll; var xScroll, yScroll;
if (self.pageYOffset) { if (self.pageYOffset) {
yScroll = self.pageYOffset; yScroll = self.pageYOffset;
xScroll = self.pageXOffset; xScroll = self.pageXOffset;
} else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict } else if (
yScroll = document.documentElement.scrollTop; document.documentElement &&
xScroll = document.documentElement.scrollLeft; document.documentElement.scrollTop
} else if (document.body) {// all other Explorers ) {
yScroll = document.body.scrollTop; // Explorer 6 Strict
xScroll = document.body.scrollLeft; yScroll = document.documentElement.scrollTop;
} xScroll = document.documentElement.scrollLeft;
arrayPageScroll = new Array(xScroll,yScroll) } else if (document.body) {
return arrayPageScroll; // all other Explorers
}; yScroll = document.body.scrollTop;
/** xScroll = document.body.scrollLeft;
* Stop the code execution from a escified time in milisecond }
* arrayPageScroll = new Array(xScroll, yScroll);
*/ return arrayPageScroll;
function ___pause(ms) { }
var date = new Date(); /**
curDate = null; * Stop the code execution from a escified time in milisecond
do { var curDate = new Date(); } *
while ( curDate - date < ms); */
}; function ___pause(ms) {
// Return the jQuery object for chaining. The unbind method is used to avoid click conflict when the plugin is called more than once var date = new Date();
return this.off('click').click(_initialize); curDate = null;
}; do {
var curDate = new Date();
} while (curDate - date < ms);
}
// Return the jQuery object for chaining. The unbind method is used to avoid click conflict when the plugin is called more than once
return this.off('click').click(_initialize);
};
})(jQuery); // Call and execute the function immediately passing the jQuery object })(jQuery); // Call and execute the function immediately passing the jQuery object

Просмотреть файл

@ -35,7 +35,7 @@ const showOrHideDefinition = (select) => {
}`; }`;
} }
} }
} };
// This function changes the visibility of the `name` field in the "change // This function changes the visibility of the `name` field in the "change
// form". We only want to show this field when a scanner is selected and this // form". We only want to show this field when a scanner is selected and this
@ -47,7 +47,7 @@ const showOrHideName = (select) => {
const isNone = selectedScannerIsNone(select); const isNone = selectedScannerIsNone(select);
const isYara = selectedScannerIsYara(select); const isYara = selectedScannerIsYara(select);
name.style.display = (isNone || isYara) ? 'none' : 'block'; name.style.display = isNone || isYara ? 'none' : 'block';
if (isYara) { if (isYara) {
const nameInput = document.querySelector('#id_name'); const nameInput = document.querySelector('#id_name');
@ -56,7 +56,7 @@ const showOrHideName = (select) => {
nameInput.value = DEFAULT_RULE_NAME; nameInput.value = DEFAULT_RULE_NAME;
} }
} }
} };
const scannerSelect = document.querySelector('#id_scanner'); const scannerSelect = document.querySelector('#id_scanner');
const definitionTextarea = document.querySelector('#id_definition'); const definitionTextarea = document.querySelector('#id_definition');

Просмотреть файл

@ -1,21 +1,22 @@
/** Addons Display page */ /** Addons Display page */
/* general initialization */ /* general initialization */
$(document).ready(function() { $(document).ready(function () {
if ($('#addon.primary').length == 0) return;
if ($('#addon.primary').length == 0) return; var lb_baseurl = z.static_url + 'img/jquery-lightbox/';
$("a[rel='jquery-lightbox']").lightBox({
overlayOpacity: 0.6,
imageBlank: lb_baseurl + 'lightbox-blank.gif',
imageLoading: lb_baseurl + 'lightbox-ico-loading.gif',
imageBtnClose: lb_baseurl + 'close.png',
imageBtnPrev: lb_baseurl + 'goleft.png',
imageBtnNext: lb_baseurl + 'goright.png',
containerResizeSpeed: 350,
});
var lb_baseurl = z.static_url+'img/jquery-lightbox/'; var etiquette_box = $('#addons-display-review-etiquette').hide();
$("a[rel='jquery-lightbox']").lightBox({ $('#short-review').focus(function () {
overlayOpacity: 0.6, etiquette_box.show('fast');
imageBlank: lb_baseurl+"lightbox-blank.gif", });
imageLoading: lb_baseurl+"lightbox-ico-loading.gif",
imageBtnClose: lb_baseurl+"close.png",
imageBtnPrev: lb_baseurl+"goleft.png",
imageBtnNext: lb_baseurl+"goright.png",
containerResizeSpeed: 350
});
var etiquette_box = $("#addons-display-review-etiquette").hide();
$("#short-review").focus(function() { etiquette_box.show("fast"); } );
}); });

Просмотреть файл

@ -1,55 +1,62 @@
(function(){ (function () {
"use strict"; 'use strict';
// Add sphinx-like links to headings with ids. // Add sphinx-like links to headings with ids.
$(function(){ $(function () {
var html = '<a class="headerlink" href="#{0}">&para;</a>'; var html = '<a class="headerlink" href="#{0}">&para;</a>';
$(':-moz-any(h1,h2,h3,h4,h5,h6)[id]').each(function() { $(':-moz-any(h1,h2,h3,h4,h5,h6)[id]').each(function () {
console.log(format(html, $(this).attr('id'))); console.log(format(html, $(this).attr('id')));
$(this).append(format(html, $(this).attr('id'))); $(this).append(format(html, $(this).attr('id')));
}); });
});
$(document).ready(function () {
$('input.searchbar').each(function () {
var $form = $(this).closest('form');
$(this).autocomplete({
minLength: 3,
width: 300,
source: function (request, response) {
$.getJSON(
$form.attr('data-search-url') + '?' + $form.serialize(),
response,
);
},
focus: function (event, ui) {
$(this).val(ui.item.label);
event.preventDefault();
},
select: function (event, ui) {
window.location = format($form.attr('action'), { id: ui.item.value });
event.preventDefault();
},
});
$form.on(
'submit',
_pd(function () {
// Prevent just submitting the form because that takes you
// to your page. TODO: do something clever with this.
}),
);
}); });
$(document).ready(function() { // Recalculate Hash
$('input.searchbar').each(function() { $('.recalc').click(
var $form = $(this).closest('form'); _pd(function () {
$(this).autocomplete({ var $this = $(this);
minLength: 3, $this.html('Recalcing&hellip;');
width: 300, $.post($this.attr('href'), function (d) {
source: function(request, response) { if (d.success) {
$.getJSON($form.attr('data-search-url') + '?' + $form.serialize(), $this.text('Done!');
response); } else {
}, $this.text('Error :(');
focus: function(event, ui) { }
$(this).val(ui.item.label); setTimeout(function () {
event.preventDefault(); $this.text('Recalc Hash');
}, }, 2000);
select: function(event, ui) {
window.location = format($form.attr('action'), {id: ui.item.value});
event.preventDefault();
}
});
$form.on('submit', _pd(function() {
// Prevent just submitting the form because that takes you
// to your page. TODO: do something clever with this.
}));
}); });
}),
// Recalculate Hash );
$('.recalc').click(_pd(function() { });
var $this = $(this); $('#id_start, #id_end').datepicker({ dateFormat: 'yy-mm-dd' });
$this.html('Recalcing&hellip;');
$.post($this.attr('href'), function(d) {
if(d.success) {
$this.text('Done!');
} else {
$this.text('Error :(');
}
setTimeout(function() {
$this.text('Recalc Hash');
}, 2000);
});
}));
});
$('#id_start, #id_end').datepicker({ dateFormat: 'yy-mm-dd' });
})(); })();

Просмотреть файл

@ -1,102 +1,135 @@
$(document).ready(function(){ $(document).ready(function () {
function incTotalForms() { function incTotalForms() {
var $totalForms = $('#id_form-TOTAL_FORMS'), var $totalForms = $('#id_form-TOTAL_FORMS'),
num = parseInt($totalForms.val()) + 1; num = parseInt($totalForms.val()) + 1;
$totalForms.val(num); $totalForms.val(num);
return num; return num;
}
// Populate cells with current collections.
$('#features td.collection').each(function () {
var $td = $(this),
cid = $td.attr('data-collection'),
$input = $td.find('.collection-ac');
if (!cid) {
$td.removeClass('loading');
$input.show();
return;
} }
$.post(
document.body.getAttribute('data-featured-collection-url'),
{ collection: cid },
function (data) {
$td.removeClass('loading');
$input.hide();
$td.find('.current-collection').html(data).show();
},
);
});
// Populate cells with current collections. $('#features').on('change', '.app select', function () {
$('#features td.collection').each(function() { // Update application id and toggle disabled attr on autocomplete field.
var $td = $(this), var $this = $(this),
cid = $td.attr('data-collection'), $tr = $this.closest('tr'),
$input = $td.find('.collection-ac'); val = $this.val();
if (!cid) { $tr.attr('data-app', val);
$td.removeClass('loading'); $tr.find('.collection-ac').prop('disabled', !val);
$input.show(); });
return; $('#features').on(
} 'click',
$.post(document.body.getAttribute('data-featured-collection-url'), '.remove',
{'collection': cid}, function(data) { _pd(function () {
$td.removeClass('loading'); $(this).closest('tr').hide();
$input.hide(); $(this).closest('td').find('input').prop('checked', true);
$td.find('.current-collection').html(data).show(); }),
}); );
}); $('#features')
.on(
$('#features').on('change', '.app select', function() { 'click',
// Update application id and toggle disabled attr on autocomplete field. '.replace',
var $this = $(this), _pd(function () {
$tr = $this.closest('tr'),
val = $this.val();
$tr.attr('data-app', val);
$tr.find('.collection-ac').prop('disabled', !val);
});
$('#features').on('click', '.remove', _pd(function() {
$(this).closest('tr').hide();
$(this).closest('td').find('input').prop('checked', true);
}));
$('#features').on('click', '.replace', _pd(function() {
var $td = $(this).closest('td'); var $td = $(this).closest('td');
$td.find('.collection-ac').show(); $td.find('.collection-ac').show();
$td.find('input[type=hidden]').val(''); $td.find('input[type=hidden]').val('');
$(this).parent().html(''); $(this).parent().html('');
})).on('collectionAdd', '.collection-ac', function() { }),
// Autocomplete for collection add form. )
var $input = $(this), .on('collectionAdd', '.collection-ac', function () {
$tr = $input.closest('tr'), // Autocomplete for collection add form.
$td = $input.closest('td'), var $input = $(this),
$select = $tr.find('.collection-select'); $tr = $input.closest('tr'),
function selectCollection() { $td = $input.closest('td'),
var item = JSON.parse($input.attr('data-item')); $select = $tr.find('.collection-select');
if (item) { function selectCollection() {
$td.find('.errorlist').remove(); var item = JSON.parse($input.attr('data-item'));
var current = template( if (item) {
'<a href="{url}" ' + $td.find('.errorlist').remove();
'class="collectionitem {is_personas}">{name} ({slug})</a>' + var current = template(
'<a href="#" class="replace">Replace with another collection</a>' '<a href="{url}" ' +
); 'class="collectionitem {is_personas}">{name} ({slug})</a>' +
$td.find('.current-collection').show().html(current({ '<a href="#" class="replace">Replace with another collection</a>',
url: item.url, );
is_personas: item.all_personas ? 'personas-collection' : '', $td
name: _.escape(item.name), .find('.current-collection')
slug: _.escape(item.slug), .show()
})); .html(
$td.find('input[type=hidden]').val(item.id); current({
$td.attr('data-collection', item.id); url: item.url,
} is_personas: item.all_personas ? 'personas-collection' : '',
$input.val(''); name: _.escape(item.name),
$input.hide(); slug: _.escape(item.slug),
}),
);
$td.find('input[type=hidden]').val(item.id);
$td.attr('data-collection', item.id);
} }
$input.autocomplete({ $input.val('');
minLength: 3, $input.hide();
width: 300, }
source: function(request, response) { $input
$.getJSON(document.body.getAttribute('data-collections-url'), .autocomplete({
{'app': $input.closest('tr').attr('data-app'), minLength: 3,
'q': request.term}, response); width: 300,
}, source: function (request, response) {
focus: function(event, ui) { $.getJSON(
$input.val(ui.item.name); document.body.getAttribute('data-collections-url'),
return false; { app: $input.closest('tr').attr('data-app'), q: request.term },
}, response,
select: function(event, ui) { );
$input.val(ui.item.name).attr('data-item', JSON.stringify(ui.item)); },
selectCollection(); focus: function (event, ui) {
return false; $input.val(ui.item.name);
} return false;
}).data('ui-autocomplete')._renderItem = function(ul, item) { },
var html = format('<a>{0} ({1})<b>ID: {2}</b></a>', [_.escape(item.name), _.escape(item.slug), item.id]); select: function (event, ui) {
return $('<li>').data('item.autocomplete', item).append(html).appendTo(ul); $input.val(ui.item.name).attr('data-item', JSON.stringify(ui.item));
}; selectCollection();
return false;
},
})
.data('ui-autocomplete')._renderItem = function (ul, item) {
var html = format('<a>{0} ({1})<b>ID: {2}</b></a>', [
_.escape(item.name),
_.escape(item.slug),
item.id,
]);
return $('<li>')
.data('item.autocomplete', item)
.append(html)
.appendTo(ul);
};
}); });
$('#features .collection-ac').trigger('collectionAdd'); $('#features .collection-ac').trigger('collectionAdd');
$('#add').click(_pd(function() { $('#add').click(
var formId = incTotalForms() - 1, _pd(function () {
emptyForm = $('tfoot').html().replace(/__prefix__/g, formId); var formId = incTotalForms() - 1,
$('tbody').append(emptyForm); emptyForm = $('tfoot')
$('tbody tr:last-child .collection-ac').trigger('collectionAdd'); .html()
})); .replace(/__prefix__/g, formId);
$('tbody').append(emptyForm);
$('tbody tr:last-child .collection-ac').trigger('collectionAdd');
}),
);
}); });

Просмотреть файл

@ -1,171 +1,193 @@
(function() { (function () {
"use strict"; 'use strict';
$(function() { $(function () {
if ($('#admin-validation').length) { if ($('#admin-validation').length) {
initAdminValidation($('#admin-validation')); initAdminValidation($('#admin-validation'));
} }
}); });
function initAdminValidation(doc) {
function initAdminValidation(doc) {
var $elem = $('#id_application', doc), var $elem = $('#id_application', doc),
statInterval, statInterval,
incompleteJobs = {}; incompleteJobs = {};
$elem.change(function(e) { $elem.change(function (e) {
var maxVer = $('#id_curr_max_version, #id_target_version', doc), var maxVer = $('#id_curr_max_version, #id_target_version', doc),
sel = $(e.target), sel = $(e.target),
appId = $('option:selected', sel).val(); appId = $('option:selected', sel).val();
if (!appId) { if (!appId) {
$('option', maxVer).remove(); $('option', maxVer).remove();
maxVer.append(format('<option value="{0}">{1}</option>', maxVer.append(
['', gettext('Select an application first')])); format('<option value="{0}">{1}</option>', [
return; '',
} gettext('Select an application first'),
$.post(sel.attr('data-url'), {'application': appId}, function(d) { ]),
$('option', maxVer).remove(); );
$.each(d.choices, function(i, ch) { return;
maxVer.append(format('<option value="{0}">{1}</option>', }
[ch[0], ch[1]])); $.post(sel.attr('data-url'), { application: appId }, function (d) {
}); $('option', maxVer).remove();
$.each(d.choices, function (i, ch) {
maxVer.append(
format('<option value="{0}">{1}</option>', [ch[0], ch[1]]),
);
}); });
});
}); });
if ($elem.children('option:selected').val() && if (
!$('#id_curr_max_version option:selected, ' + $elem.children('option:selected').val() &&
'#id_target_version option:selected', doc).val()) { !$(
// If an app is selected when page loads and it's not a form post. '#id_curr_max_version option:selected, ' +
$elem.trigger('change'); '#id_target_version option:selected',
doc,
).val()
) {
// If an app is selected when page loads and it's not a form post.
$elem.trigger('change');
} }
var $popup = $('#notify').popup('td a.v-popup', { var $popup = $('#notify').popup('td a.v-popup', {
width: '600px', width: '600px',
callback: function(obj) { callback: function (obj) {
var $ct = $(obj.click_target), var $ct = $(obj.click_target),
msg = '', msg = '',
// L10n: {0} is the number of add-ons, {1} is a version like 4.0 // L10n: {0} is the number of add-ons, {1} is a version like 4.0
msg = ngettext('Set {0} add-on to a max version of {1} and email the author.', msg =
'Set {0} add-ons to a max version of {1} and email the authors.', ngettext(
$ct.attr('data-job-count-passing')) + ' ' + 'Set {0} add-on to a max version of {1} and email the author.',
ngettext('Email author of {2} add-on which failed validation.', 'Set {0} add-ons to a max version of {1} and email the authors.',
'Email authors of {2} add-ons which failed validation.', $ct.attr('data-job-count-passing'),
$ct.attr('data-job-count-failing')); ) +
' ' +
ngettext(
'Email author of {2} add-on which failed validation.',
'Email authors of {2} add-ons which failed validation.',
$ct.attr('data-job-count-failing'),
);
msg = format(msg, [$ct.attr('data-job-count-passing'), $ct.attr('data-job-version'), msg = format(msg, [
$ct.attr('data-job-count-failing')]); $ct.attr('data-job-count-passing'),
$(this).find('p.error').text(''); // clear any existing errors. $ct.attr('data-job-version'),
$(this).find('p').eq(0).text(msg); $ct.attr('data-job-count-failing'),
$(this).children('form').attr('action', $ct.attr('data-job-url')); ]);
return { pointTo: $ct }; $(this).find('p.error').text(''); // clear any existing errors.
} $(this).find('p').eq(0).text(msg);
$(this).children('form').attr('action', $ct.attr('data-job-url'));
return { pointTo: $ct };
},
}); });
$('#notify form').submit(function(e) { $('#notify form').submit(function (e) {
var $form = $(this); var $form = $(this);
if ($form.attr('data-valid') != 'valid') { if ($form.attr('data-valid') != 'valid') {
$.post($form.attr('data-url'), $(this).serialize(), function(json) { $.post($form.attr('data-url'), $(this).serialize(), function (json) {
if (json && json.valid) { if (json && json.valid) {
$form.attr('data-valid', 'valid').submit(); $form.attr('data-valid', 'valid').submit();
} else { } else {
$form.find('p.error').text(json.error).show(); $form.find('p.error').text(json.error).show();
} }
}); });
e.preventDefault(); e.preventDefault();
return false; return false;
} else { } else {
return true; return true;
} }
}); });
$('#notify form span.cancel a').click(_pd(function() { $('#notify form span.cancel a').click(
_pd(function () {
$popup.hideMe(); $popup.hideMe();
})); }),
);
function startStats() { function startStats() {
var incompleteJobIds = [], var incompleteJobIds = [],
checkStatus; checkStatus;
$('tr.job-result').each(function(i, el) { $('tr.job-result').each(function (i, el) {
var $el = $(el), var $el = $(el),
$td = $el.children('td.tests-finished'), $td = $el.children('td.tests-finished'),
isComplete = parseInt($el.attr('data-is-complete'), 10), isComplete = parseInt($el.attr('data-is-complete'), 10),
jobId = parseInt($el.attr('data-job-id'), 10); jobId = parseInt($el.attr('data-job-id'), 10);
if (!isComplete) { if (!isComplete) {
incompleteJobIds.push(jobId); incompleteJobIds.push(jobId);
incompleteJobs[jobId] = $td; incompleteJobs[jobId] = $td;
createProgressBar($td); createProgressBar($td);
}
});
if (incompleteJobIds.length) {
var checkStatus = function() {
$('#admin-validation').trigger('checkstats', [incompleteJobIds]);
};
checkStatus();
statInterval = setInterval(checkStatus, 3000);
} }
});
if (incompleteJobIds.length) {
var checkStatus = function () {
$('#admin-validation').trigger('checkstats', [incompleteJobIds]);
};
checkStatus();
statInterval = setInterval(checkStatus, 3000);
}
} }
startStats(); startStats();
$('td').on('receivestats', function(ev, stats) { $('td').on('receivestats', function (ev, stats) {
var $el = $(this), var $el = $(this),
$tr = $el.parent(), $tr = $el.parent(),
complete = stats.percent_complete; complete = stats.percent_complete;
$tr.children('td.tested').text(stats.total); $tr.children('td.tested').text(stats.total);
$tr.children('td.failing').text(stats.failing); $tr.children('td.failing').text(stats.failing);
$tr.children('td.passing').text(stats.passing); $tr.children('td.passing').text(stats.passing);
$tr.children('td.exceptions').text(stats.errors); $tr.children('td.exceptions').text(stats.errors);
$('.job-status-bar div', $el).animate({'width': complete + '%'}, $('.job-status-bar div', $el).animate(
{duration: 500}); { width: complete + '%' },
if (stats.completed_timestamp != '') { { duration: 500 },
delete incompleteJobs[stats.job_id]; );
$('.job-status-bar', $el).remove(); if (stats.completed_timestamp != '') {
$el.text(stats.completed_timestamp); delete incompleteJobs[stats.job_id];
jobCompleted(); $('.job-status-bar', $el).remove();
} $el.text(stats.completed_timestamp);
jobCompleted();
}
}); });
$('#admin-validation').on('checkstats', function(ev, job_ids) { $('#admin-validation').on('checkstats', function (ev, job_ids) {
$.ajax({type: 'POST', $.ajax({
url: $(this).attr('data-status-url'), type: 'POST',
data: {job_ids: JSON.stringify(job_ids)}, url: $(this).attr('data-status-url'),
cache: false, data: { job_ids: JSON.stringify(job_ids) },
success: function(data) { cache: false,
$.each(data, function(jobId, stats) { success: function (data) {
if (incompleteJobs[jobId]) { $.each(data, function (jobId, stats) {
incompleteJobs[jobId].trigger('receivestats', [stats]); if (incompleteJobs[jobId]) {
} else { incompleteJobs[jobId].trigger('receivestats', [stats]);
if (typeof console !== 'undefined') } else {
console.log('checkstats: Job ID does not exist: ' + jobId); if (typeof console !== 'undefined')
} console.log('checkstats: Job ID does not exist: ' + jobId);
}); }
}, });
error: function(XMLHttpRequest, textStatus, errorThrown) { },
if (typeof console !== 'undefined') error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log('error: ' + textStatus); if (typeof console !== 'undefined')
}, console.log('error: ' + textStatus);
dataType: 'json' },
}); dataType: 'json',
});
}); });
function createProgressBar($el) { function createProgressBar($el) {
var bar = {}; var bar = {};
bar.progress_outside = $('<div>', {'class': 'job-status-bar'}); bar.progress_outside = $('<div>', { class: 'job-status-bar' });
bar.progress_inside = $('<div>').css('width', 0); bar.progress_inside = $('<div>').css('width', 0);
bar.progress_outside.append(bar.progress_inside); bar.progress_outside.append(bar.progress_inside);
$el.append(bar.progress_outside); $el.append(bar.progress_outside);
bar.progress_outside.show(); bar.progress_outside.show();
} }
function jobCompleted() { function jobCompleted() {
var allDone = true; var allDone = true;
$.each(incompleteJobs, function(jobId, el) { $.each(incompleteJobs, function (jobId, el) {
allDone = false; allDone = false;
}); });
if (allDone) { if (allDone) {
clearInterval(statInterval); clearInterval(statInterval);
} }
} }
} }
})(); })();

Просмотреть файл

@ -9,7 +9,7 @@ function isDoNotTrackEnabled() {
if (dnt === '1') { if (dnt === '1') {
window.console && window.console &&
console.info( console.info(
'[TRACKING]: Do Not Track Enabled; Google Analytics will not be loaded.' '[TRACKING]: Do Not Track Enabled; Google Analytics will not be loaded.',
); );
return true; return true;
} }
@ -36,7 +36,7 @@ if (isDoNotTrackEnabled() === false) {
document, document,
'script', 'script',
'https://www.google-analytics.com/analytics.js', 'https://www.google-analytics.com/analytics.js',
'ga' 'ga',
); );
ga('create', 'UA-36116321-7', 'auto'); ga('create', 'UA-36116321-7', 'auto');

Просмотреть файл

@ -1,163 +1,159 @@
/* Browser Utilities /* Browser Utilities
* Based on amo2009/addons.js * Based on amo2009/addons.js
**/ **/
function BrowserUtils() { function BrowserUtils() {
"use strict"; 'use strict';
var userAgentStrings = { var userAgentStrings = {
'firefox' : /^Mozilla.*(Firefox|Minefield|Namoroka|Shiretoko|GranParadiso|BonEcho|Iceweasel|Fennec|MozillaDeveloperPreview)\/([^\s]*).*$/, firefox: /^Mozilla.*(Firefox|Minefield|Namoroka|Shiretoko|GranParadiso|BonEcho|Iceweasel|Fennec|MozillaDeveloperPreview)\/([^\s]*).*$/,
'seamonkey': /^Mozilla.*(SeaMonkey|Iceape)\/([^\s]*).*$/, seamonkey: /^Mozilla.*(SeaMonkey|Iceape)\/([^\s]*).*$/,
'mobile': /^Mozilla.*(Fennec|Mobile)\/([^\s]*)$/, mobile: /^Mozilla.*(Fennec|Mobile)\/([^\s]*)$/,
'thunderbird': /^Mozilla.*(Thunderbird|Shredder|Lanikai)\/([^\s*]*).*$/ thunderbird: /^Mozilla.*(Thunderbird|Shredder|Lanikai)\/([^\s*]*).*$/,
}, },
osStrings = { osStrings = {
'windows': /Windows/, windows: /Windows/,
'mac': /Mac/, mac: /Mac/,
'linux': /Linux|BSD/, linux: /Linux|BSD/,
'android': /Android/, android: /Android/,
};
// browser detection
var browser = {},
browserVersion = '',
pattern, match, i,
badBrowser = true;
for (i in userAgentStrings) {
if (userAgentStrings.hasOwnProperty(i)) {
pattern = userAgentStrings[i];
match = pattern.exec(navigator.userAgent);
browser[i] = !!(match && match.length === 3);
if (browser[i]) {
browserVersion = escape_(match[2]);
badBrowser = false;
}
}
}
// Seamonkey looks like Firefox but Firefox doesn't look like Seamonkey.
// If both are true, set Firefox to false.
if (browser.firefox && browser.seamonkey) {
browser.firefox = false;
}
var os = {},
platform = "";
for (i in osStrings) {
if (osStrings.hasOwnProperty(i)) {
pattern = osStrings[i];
os[i] = pattern.test(navigator.userAgent);
if (os[i]) {
platform = i;
}
}
}
if (!platform) {
os['other'] = !platform;
platform = "other";
}
return {
"browser": browser,
"browserVersion": browserVersion,
"badBrowser": badBrowser,
"os": os,
"platform": platform,
}; };
// browser detection
var browser = {},
browserVersion = '',
pattern,
match,
i,
badBrowser = true;
for (i in userAgentStrings) {
if (userAgentStrings.hasOwnProperty(i)) {
pattern = userAgentStrings[i];
match = pattern.exec(navigator.userAgent);
browser[i] = !!(match && match.length === 3);
if (browser[i]) {
browserVersion = escape_(match[2]);
badBrowser = false;
}
}
}
// Seamonkey looks like Firefox but Firefox doesn't look like Seamonkey.
// If both are true, set Firefox to false.
if (browser.firefox && browser.seamonkey) {
browser.firefox = false;
}
var os = {},
platform = '';
for (i in osStrings) {
if (osStrings.hasOwnProperty(i)) {
pattern = osStrings[i];
os[i] = pattern.test(navigator.userAgent);
if (os[i]) {
platform = i;
}
}
}
if (!platform) {
os['other'] = !platform;
platform = 'other';
}
return {
browser: browser,
browserVersion: browserVersion,
badBrowser: badBrowser,
os: os,
platform: platform,
};
} }
var VersionCompare = { var VersionCompare = {
/** /**
* Mozilla-style version numbers comparison in Javascript * Mozilla-style version numbers comparison in Javascript
* (JS-translated version of PHP versioncompare component) * (JS-translated version of PHP versioncompare component)
* @return -1: a<b, 0: a==b, 1: a>b * @return -1: a<b, 0: a==b, 1: a>b
*/ */
compareVersions: function(a,b) { compareVersions: function (a, b) {
var al = a.split('.'), var al = a.split('.'),
bl = b.split('.'), bl = b.split('.'),
ap, bp, r, i; ap,
for (i=0; i<al.length || i<bl.length; i++) { bp,
ap = (i<al.length ? al[i] : null); r,
bp = (i<bl.length ? bl[i] : null); i;
r = this.compareVersionParts(ap,bp); for (i = 0; i < al.length || i < bl.length; i++) {
if (r !== 0) ap = i < al.length ? al[i] : null;
return r; bp = i < bl.length ? bl[i] : null;
} r = this.compareVersionParts(ap, bp);
return 0; if (r !== 0) return r;
},
/**
* helper function: compare a single version part
*/
compareVersionParts: function(ap,bp) {
var avp = this.parseVersionPart(ap),
bvp = this.parseVersionPart(bp),
r = this.cmp(avp['numA'],bvp['numA']);
if (r) return r;
r = this.strcmp(avp['strB'],bvp['strB']);
if (r) return r;
r = this.cmp(avp['numC'],bvp['numC']);
if (r) return r;
return this.strcmp(avp['extraD'],bvp['extraD']);
},
/**
* helper function: parse a version part
*/
parseVersionPart: function(p) {
if (p == '*') {
return {
'numA' : Number.MAX_VALUE,
'strB' : '',
'numC' : 0,
'extraD' : ''
};
}
var pattern = /^([-\d]*)([^-\d]*)([-\d]*)(.*)$/,
m = pattern.exec(p),
r = {
'numA' : parseInt(m[1], 10),
'strB' : m[2],
'numC' : parseInt(m[3], 10),
'extraD' : m[4]
};
if (r['strB'] == '+') {
r['numA']++;
r['strB'] = 'pre';
}
return r;
},
/**
* helper function: compare numeric version parts
*/
cmp: function(an,bn) {
if (isNaN(an)) an = 0;
if (isNaN(bn)) bn = 0;
if (an < bn)
return -1;
if (an > bn)
return 1;
return 0;
},
/**
* helper function: compare string version parts
*/
strcmp: function(as,bs) {
if (as == bs)
return 0;
// any string comes *before* the empty string
if (as === '')
return 1;
if (bs === '')
return -1;
// normal string comparison for non-empty strings (like strcmp)
if (as < bs)
return -1;
else if(as > bs)
return 1;
else
return 0;
} }
return 0;
},
/**
* helper function: compare a single version part
*/
compareVersionParts: function (ap, bp) {
var avp = this.parseVersionPart(ap),
bvp = this.parseVersionPart(bp),
r = this.cmp(avp['numA'], bvp['numA']);
if (r) return r;
r = this.strcmp(avp['strB'], bvp['strB']);
if (r) return r;
r = this.cmp(avp['numC'], bvp['numC']);
if (r) return r;
return this.strcmp(avp['extraD'], bvp['extraD']);
},
/**
* helper function: parse a version part
*/
parseVersionPart: function (p) {
if (p == '*') {
return {
numA: Number.MAX_VALUE,
strB: '',
numC: 0,
extraD: '',
};
}
var pattern = /^([-\d]*)([^-\d]*)([-\d]*)(.*)$/,
m = pattern.exec(p),
r = {
numA: parseInt(m[1], 10),
strB: m[2],
numC: parseInt(m[3], 10),
extraD: m[4],
};
if (r['strB'] == '+') {
r['numA']++;
r['strB'] = 'pre';
}
return r;
},
/**
* helper function: compare numeric version parts
*/
cmp: function (an, bn) {
if (isNaN(an)) an = 0;
if (isNaN(bn)) bn = 0;
if (an < bn) return -1;
if (an > bn) return 1;
return 0;
},
/**
* helper function: compare string version parts
*/
strcmp: function (as, bs) {
if (as == bs) return 0;
// any string comes *before* the empty string
if (as === '') return 1;
if (bs === '') return -1;
// normal string comparison for non-empty strings (like strcmp)
if (as < bs) return -1;
else if (as > bs) return 1;
else return 0;
},
}; };

Просмотреть файл

@ -1,320 +1,361 @@
(function() { (function () {
/* Call this with something like $('.install').installButton(); */
z.button = {};
/* Call this with something like $('.install').installButton(); */ /* A library of callbacks that may be run after InstallTrigger succeeds.
z.button = {}; * ``this`` will be bound to the .install button.
*/
/* A library of callbacks that may be run after InstallTrigger succeeds. z.button.after = {
* ``this`` will be bound to the .install button. contrib: function (xpi_url, status) {
*/ if (status === 0) {
z.button.after = {'contrib': function(xpi_url, status) { //success
if (status === 0) { //success
document.location = $(this).attr('data-developers'); document.location = $(this).attr('data-developers');
} }
}}; },
};
var notavail = '<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>', var notavail =
incompat = '<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>', '<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>',
noappsupport = '<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>', incompat =
'<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>',
noappsupport =
'<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>',
download_re = new RegExp('(/downloads/(?:latest|file)/\\d+)'); download_re = new RegExp('(/downloads/(?:latest|file)/\\d+)');
// The lowest maxVersion an app has to support to allow default-to-compatible. // The lowest maxVersion an app has to support to allow default-to-compatible.
var D2C_MAX_VERSIONS = { var D2C_MAX_VERSIONS = {
firefox: '4.0', firefox: '4.0',
mobile: '11.0', mobile: '11.0',
seamonkey: '2.1', seamonkey: '2.1',
thunderbird: '5.0' thunderbird: '5.0',
}; };
/* Called by the jQuery plugin to set up a single button. */ /* Called by the jQuery plugin to set up a single button. */
var installButton = function() { var installButton = function () {
// Create a bunch of data and helper functions, then drive the buttons // Create a bunch of data and helper functions, then drive the buttons
// based on the button type at the end. // based on the button type at the end.
var self = this, var self = this,
$this = $(this), $this = $(this),
$button = $this.find('.button'); $button = $this.find('.button');
// Unreviewed and self-hosted buttons point to the add-on detail page for // Unreviewed and self-hosted buttons point to the add-on detail page for
// non-js safety. Flip them to the real xpi url here. // non-js safety. Flip them to the real xpi url here.
$button.each(function() { $button.each(function () {
var $this = $(this); var $this = $(this);
if ($this.hasattr('data-realurl')) { if ($this.hasattr('data-realurl')) {
$this.attr('href', $this.attr('data-realurl')); $this.attr('href', $this.attr('data-realurl'));
} }
/* If we're on the mobile site but it's not a mobile browser, force /* If we're on the mobile site but it's not a mobile browser, force
* the download url to type:attachment. * the download url to type:attachment.
*/ */
if (z.app === 'mobile' && !z.appMatchesUserAgent) { if (z.app === 'mobile' && !z.appMatchesUserAgent) {
var href = $this.attr('href'); var href = $this.attr('href');
$this.attr('href', href.replace(download_re, '$1/type:attachment')); $this.attr('href', href.replace(download_re, '$1/type:attachment'));
} }
}); });
var addon = $this.attr('data-addon'), var addon = $this.attr('data-addon'),
min = $this.attr('data-min'), min = $this.attr('data-min'),
max = $this.attr('data-max'), max = $this.attr('data-max'),
name = $this.attr('data-name'), name = $this.attr('data-name'),
icon = $this.attr('data-icon'), icon = $this.attr('data-icon'),
after = $this.attr('data-after'), after = $this.attr('data-after'),
search = $this.hasattr('data-search'), search = $this.hasattr('data-search'),
no_compat_necessary = $this.hasattr('data-no-compat-necessary'), no_compat_necessary = $this.hasattr('data-no-compat-necessary'),
accept_eula = $this.hasClass('accept'), accept_eula = $this.hasClass('accept'),
compatible = $this.attr('data-is-compatible-by-default') == 'true', compatible = $this.attr('data-is-compatible-by-default') == 'true',
compatible_app = $this.attr('data-is-compatible-app') == 'true', compatible_app = $this.attr('data-is-compatible-app') == 'true',
has_overrides = $this.hasattr('data-compat-overrides'), has_overrides = $this.hasattr('data-compat-overrides'),
versions_url = $this.attr('data-versions'), versions_url = $this.attr('data-versions'),
// L10n: {0} is an app name like Firefox. // L10n: {0} is an app name like Firefox.
_s = accept_eula ? gettext('Accept and Install') : gettext('Add to {0}'), _s = accept_eula ? gettext('Accept and Install') : gettext('Add to {0}'),
addto = format(_s, [z.appName]), addto = format(_s, [z.appName]),
appSupported = z.appMatchesUserAgent && min && max, appSupported = z.appMatchesUserAgent && min && max,
$body = $(document.body), $body = $(document.body),
olderBrowser, olderBrowser,
newerBrowser; newerBrowser;
// If we have os-specific buttons, check that one of them matches the // If we have os-specific buttons, check that one of them matches the
// current platform. // current platform.
var badPlatform = ($button.find('.os').length && var badPlatform =
!$button.hasClass(z.platform)); $button.find('.os').length && !$button.hasClass(z.platform);
// min and max only exist if the add-on is compatible with request[APP]. // min and max only exist if the add-on is compatible with request[APP].
if (appSupported) { if (appSupported) {
// The user *has* an older/newer browser. // The user *has* an older/newer browser.
olderBrowser = VersionCompare.compareVersions(z.browserVersion, min) < 0; olderBrowser = VersionCompare.compareVersions(z.browserVersion, min) < 0;
newerBrowser = VersionCompare.compareVersions(z.browserVersion, max) > 0; newerBrowser = VersionCompare.compareVersions(z.browserVersion, max) > 0;
if (olderBrowser) { if (olderBrowser) {
// Make sure we show the "Not available for ..." messaging. // Make sure we show the "Not available for ..." messaging.
compatible = false; compatible = false;
} }
} }
// Default to compatible checking. // Default to compatible checking.
if (compatible) { if (compatible) {
if (!compatible_app) { if (!compatible_app) {
compatible = false;
}
// If it's still compatible, check the overrides.
if (compatible && has_overrides) {
var overrides = JSON.parse($this.attr('data-compat-overrides'));
_.each(overrides, function(override) {
var _min = override[0],
_max = override[1];
if (VersionCompare.compareVersions(z.browserVersion, _min) >= 0 &&
VersionCompare.compareVersions(z.browserVersion, _max) <= 0) {
compatible = false;
return;
}
});
}
} else {
compatible = false; compatible = false;
}
// If it's still compatible, check the overrides.
if (compatible && has_overrides) {
var overrides = JSON.parse($this.attr('data-compat-overrides'));
_.each(overrides, function (override) {
var _min = override[0],
_max = override[1];
if (
VersionCompare.compareVersions(z.browserVersion, _min) >= 0 &&
VersionCompare.compareVersions(z.browserVersion, _max) <= 0
) {
compatible = false;
return;
}
});
}
} else {
compatible = false;
} }
var addWarning = function(msg, type) { var addWarning = function (msg, type) {
$this.parent().append(format(type || notavail, [msg])); $this.parent().append(format(type || notavail, [msg]));
}; };
// Change the button text to "Add to Firefox". // Change the button text to "Add to Firefox".
var addToApp = function() { var addToApp = function () {
if (appSupported || (no_compat_necessary && z.appMatchesUserAgent)) { if (appSupported || (no_compat_necessary && z.appMatchesUserAgent)) {
$button.addClass('add').removeClass('download') $button
.find('span').text(addto); .addClass('add')
} .removeClass('download')
.find('span')
.text(addto);
}
}; };
// Calls InstallTrigger.install or AddSearchProvider if we capture a click // Calls InstallTrigger.install or AddSearchProvider if we capture a click
// on something with a .installer class. // on something with a .installer class.
var clickHijack = function() { var clickHijack = function () {
try { try {
if (!appSupported && !no_compat_necessary || !("InstallTrigger" in window)) return; if (
} catch (e) { (!appSupported && !no_compat_necessary) ||
return; !('InstallTrigger' in window)
} )
return;
} catch (e) {
return;
}
$this.addClass('clickHijack'); // So we can disable pointer events $this.addClass('clickHijack'); // So we can disable pointer events
$this.on('mousedown focus', function(e) { $this
$this.addClass('active'); .on('mousedown focus', function (e) {
}).on('mouseup blur', function(e) { $this.addClass('active');
$this.removeClass('active'); })
}).click(function(e) { .on('mouseup blur', function (e) {
// If the click was on a.installer or a child, call the special $this.removeClass('active');
// install method. We can't bind this directly because we add })
// more .installers dynamically. .click(function (e) {
var $target = $(e.target), // If the click was on a.installer or a child, call the special
$installer = ''; // install method. We can't bind this directly because we add
if ($target.hasClass('installer')) { // more .installers dynamically.
installer = $target; var $target = $(e.target),
} else { $installer = '';
installer = $target.parents('.installer').first(); if ($target.hasClass('installer')) {
if (_.indexOf($this.find('.installer'), installer[0]) == -1) { installer = $target;
return; } else {
} installer = $target.parents('.installer').first();
if (_.indexOf($this.find('.installer'), installer[0]) == -1) {
return;
} }
e.preventDefault(); }
e.preventDefault();
// map download url => file hash. // map download url => file hash.
var hashes = {}; var hashes = {};
$this.find('.button[data-hash]').each(function() { $this.find('.button[data-hash]').each(function () {
hashes[$(this).attr('href')] = $(this).attr('data-hash'); hashes[$(this).attr('href')] = $(this).attr('data-hash');
}); });
var hash = hashes[installer.attr('href')]; var hash = hashes[installer.attr('href')];
var f = _.haskey(z.button.after, after) ? z.button.after[after] : _.identity, var f = _.haskey(z.button.after, after)
callback = _.bind(f, self), ? z.button.after[after]
install = search ? z.installSearch : z.installAddon; : _.identity,
install(name, installer[0].href, icon, hash, callback); callback = _.bind(f, self),
install = search ? z.installSearch : z.installAddon;
install(name, installer[0].href, icon, hash, callback);
}); });
}; };
// Gather the available platforms. // Gather the available platforms.
var platforms = $button.map(function() { var platforms = $button.map(function () {
var name = $(this).find('.os').attr('data-os'), var name = $(this).find('.os').attr('data-os'),
text = z.appMatchesUserAgent ? text = z.appMatchesUserAgent
/* L10n: {0} is an platform like Windows or Linux. */ ? /* L10n: {0} is an platform like Windows or Linux. */
gettext('Install for {0} anyway') : gettext('Download for {0} anyway'); gettext('Install for {0} anyway')
return { : gettext('Download for {0} anyway');
href: $(this).attr('href'), return {
msg: format(text, [name]) href: $(this).attr('href'),
}; msg: format(text, [name]),
};
}); });
var showDownloadAnyway = function($button) { var showDownloadAnyway = function ($button) {
var $visibleButton = $button.filter(':visible') var $visibleButton = $button.filter(':visible');
var $installShell = $visibleButton.parents('.install-shell'); var $installShell = $visibleButton.parents('.install-shell');
var $downloadAnyway = $visibleButton.next('.download-anyway'); var $downloadAnyway = $visibleButton.next('.download-anyway');
if ($downloadAnyway.length) { if ($downloadAnyway.length) {
// We want to be able to add the download anyway link regardless // We want to be able to add the download anyway link regardless
// of what is already shown. There could be just an error message, // of what is already shown. There could be just an error message,
// or an error message plus a link to more versions. We also want // or an error message plus a link to more versions. We also want
// those combinations to work without the download anyway link // those combinations to work without the download anyway link
// being shown. // being shown.
// Append a separator to the .more-versions element: // Append a separator to the .more-versions element:
// if it's displayed we need to separate the download anyway link // if it's displayed we need to separate the download anyway link
// from the text shown in that span. // from the text shown in that span.
var $moreVersions = $installShell.find('.more-versions'); var $moreVersions = $installShell.find('.more-versions');
$moreVersions.append(' | '); $moreVersions.append(' | ');
// In any case, add the download anyway link to the parent div. // In any case, add the download anyway link to the parent div.
// It'll show up regardless of whether we are showing the more // It'll show up regardless of whether we are showing the more
// versions link or not. // versions link or not.
var $newParent = $installShell.find('.extra .not-available'); var $newParent = $installShell.find('.extra .not-available');
$newParent.append($downloadAnyway); $newParent.append($downloadAnyway);
$downloadAnyway.show(); $downloadAnyway.show();
} }
} };
// Add version and platform warnings. This is one // Add version and platform warnings. This is one
// big function since we merge the messaging when bad platform and version // big function since we merge the messaging when bad platform and version
// occur simultaneously. // occur simultaneously.
var versionsAndPlatforms = function(options) { var versionsAndPlatforms = function (options) {
var opts = $.extend({addWarning: true}, options); var opts = $.extend({ addWarning: true }, options);
warn = opts.addWarning ? addWarning : _.identity; warn = opts.addWarning ? addWarning : _.identity;
// Do badPlatform prep out here since we need it in all branches. // Do badPlatform prep out here since we need it in all branches.
if (badPlatform) { if (badPlatform) {
warn(gettext('Not available for your platform')); warn(gettext('Not available for your platform'));
$button.addClass('concealed'); $button.addClass('concealed');
$button.first().css('display', 'inherit'); $button.first().css('display', 'inherit');
$button.closest('.item.addon').addClass('incompatible'); $button.closest('.item.addon').addClass('incompatible');
}
if (appSupported && !compatible && (olderBrowser || newerBrowser)) {
// L10n: {0} is an app name.
var msg = format(
gettext('This add-on is not compatible with your version of {0}.'),
[z.appName, z.browserVersion],
);
var tpl = template(
msg +
' <br/><span class="more-versions"><a href="{versions_url}">' +
gettext('View other versions') +
'</a></span>',
);
warn(tpl({ versions_url: versions_url }));
$button.closest('div').attr('data-version-supported', false);
$button.addClass('concealed');
$button.closest('.item.addon').addClass('incompatible');
if (!badPlatform) {
showDownloadAnyway($button);
} }
if (appSupported && !compatible && (olderBrowser || newerBrowser)) { return true;
// L10n: {0} is an app name. } else if (!unreviewed && (appSupported || no_compat_necessary)) {
var msg = format(gettext('This add-on is not compatible with your version of {0}.'), // Good version, good platform.
[z.appName, z.browserVersion]); $button.addClass('installer');
var tpl = template(msg + $button.closest('div').attr('data-version-supported', true);
' <br/><span class="more-versions"><a href="{versions_url}">' + } else if (!appSupported) {
gettext('View other versions') + '</a></span>'); var msg =
warn(tpl({'versions_url': versions_url})); min && max
? gettext('Works with {app} {min} - {max}')
$button.closest('div').attr('data-version-supported', false); : gettext('Works with {app}');
$button.addClass('concealed'); var tpl = template(
$button.closest('.item.addon').addClass('incompatible'); msg +
if (!badPlatform) { '<br/><span class="more-versions"><a href="{versions_url}">' +
showDownloadAnyway($button); gettext('View other versions') +
} '</a></span>',
);
return true; var context = {
} else if (!unreviewed && (appSupported || no_compat_necessary)) { app: z.appName,
// Good version, good platform. min: min,
$button.addClass('installer'); max: max,
$button.closest('div').attr('data-version-supported', true); versions_url: versions_url,
} else if (!appSupported) { };
var msg = (min && max ? addWarning(tpl(context), noappsupport);
gettext('Works with {app} {min} - {max}') : if (!badPlatform) {
gettext('Works with {app}')); showDownloadAnyway($button);
var tpl = template(msg +
'<br/><span class="more-versions"><a href="{versions_url}">' +
gettext('View other versions') + '</a></span>');
var context = {'app': z.appName, 'min': min, 'max': max,
'versions_url': versions_url};
addWarning(tpl(context), noappsupport);
if (!badPlatform) {
showDownloadAnyway($button);
}
} }
return false; }
return false;
}; };
// What kind of button are we dealing with? // What kind of button are we dealing with?
var unreviewed = $this.hasClass('unreviewed'), var unreviewed = $this.hasClass('unreviewed'),
contrib = $this.hasClass('contrib'), contrib = $this.hasClass('contrib'),
eula = $this.hasClass('eula'); eula = $this.hasClass('eula');
// Drive the install button based on its type. // Drive the install button based on its type.
if (eula || contrib) { if (eula || contrib) {
versionsAndPlatforms(); versionsAndPlatforms();
} else if (z.appMatchesUserAgent) { } else if (z.appMatchesUserAgent) {
clickHijack(); clickHijack();
addToApp(); addToApp();
var opts = no_compat_necessary ? {addWarning: false} : {}; var opts = no_compat_necessary ? { addWarning: false } : {};
versionsAndPlatforms(opts); versionsAndPlatforms(opts);
} else if (z.app == 'firefox') { } else if (z.app == 'firefox') {
$button.addClass('CTA'); $button.addClass('CTA');
$button.text(gettext('Only with Firefox \u2014 Get Firefox Now!')); $button.text(gettext('Only with Firefox \u2014 Get Firefox Now!'));
$button.attr('href', 'https://www.mozilla.org/firefox/new/?scene=2&utm_source=addons.mozilla.org&utm_medium=referral&utm_campaign=non-fx-button#download-fx'); $button.attr(
$('#site-nonfx').hide(); 'href',
'https://www.mozilla.org/firefox/new/?scene=2&utm_source=addons.mozilla.org&utm_medium=referral&utm_campaign=non-fx-button#download-fx',
);
$('#site-nonfx').hide();
} else if (z.app == 'thunderbird') { } else if (z.app == 'thunderbird') {
versionsAndPlatforms(); versionsAndPlatforms();
} else { } else {
clickHijack(); clickHijack();
addToApp(); addToApp();
versionsAndPlatforms(); versionsAndPlatforms();
} }
}; };
jQuery.fn.installButton = function() { jQuery.fn.installButton = function () {
return this.each(installButton); return this.each(installButton);
}; };
/* Install an XPI or a JAR (or something like that). /* Install an XPI or a JAR (or something like that).
* *
* hash and callback are optional. callback is triggered after the * hash and callback are optional. callback is triggered after the
* installation is complete. * installation is complete.
*/ */
z.installAddon = function(name, url, icon, hash, callback) { z.installAddon = function (name, url, icon, hash, callback) {
var params = {}; var params = {};
params[name] = { params[name] = {
URL: url, URL: url,
IconURL: icon, IconURL: icon,
toString: function() { return url; } toString: function () {
return url;
},
}; };
if (hash) { if (hash) {
params[name].Hash = hash; params[name].Hash = hash;
} }
// InstallTrigger is a Gecko API. // InstallTrigger is a Gecko API.
InstallTrigger.install(params, callback); InstallTrigger.install(params, callback);
ga('send', 'event', 'AMO Addon / Theme Installs', 'addon', name); ga('send', 'event', 'AMO Addon / Theme Installs', 'addon', name);
}; };
z.installSearch = function (name, url, icon, hash, callback) {
z.installSearch = function(name, url, icon, hash, callback) {
if (window.external && window.external.AddSearchProvider) { if (window.external && window.external.AddSearchProvider) {
window.external.AddSearchProvider(url); window.external.AddSearchProvider(url);
callback(); callback();
ga('send', 'event', 'AMO Addon / Theme Installs', 'addon', name); ga('send', 'event', 'AMO Addon / Theme Installs', 'addon', name);
} else { } else {
// Alert! Deal with it. // Alert! Deal with it.
alert(gettext('Sorry, you need a Mozilla-based browser (such as Firefox) to install a search plugin.')); alert(
gettext(
'Sorry, you need a Mozilla-based browser (such as Firefox) to install a search plugin.',
),
);
} }
}; };
})(); })();

Просмотреть файл

@ -5,13 +5,15 @@
// context // context
function debounce(fn, ms, ctxt) { function debounce(fn, ms, ctxt) {
var ctx = ctxt || window; var ctx = ctxt || window;
var to, del = ms, fun = fn; var to,
return function () { del = ms,
var args = arguments; fun = fn;
clearTimeout(to); return function () {
to = setTimeout(function() { var args = arguments;
fun.apply(ctx, args); clearTimeout(to);
}, del); to = setTimeout(function () {
}; fun.apply(ctx, args);
}; }, del);
};
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,40 +1,44 @@
google.load('search', '1', {'language' : $('html').attr('lang')}); google.load('search', '1', { language: $('html').attr('lang') });
google.setOnLoadCallback(function() { google.setOnLoadCallback(function () {
var qry = $('.header-search input[name="q"]'), var qry = $('.header-search input[name="q"]'),
opt = new google.search.DrawOptions(); opt = new google.search.DrawOptions();
opt.setInput(qry.get(0)); opt.setInput(qry.get(0));
sc = new google.search.CustomSearchControl('007182852441266509516:fnsg3w7luc4'); sc = new google.search.CustomSearchControl(
sc.setNoResultsString(gettext('No results found.')); '007182852441266509516:fnsg3w7luc4',
sc.setSearchStartingCallback(null, function(sc, searcher, qry) { );
sc.maxResultCount = 0; sc.setNoResultsString(gettext('No results found.'));
}); sc.setSearchStartingCallback(null, function (sc, searcher, qry) {
sc.maxResultCount = 0;
});
sc.setSearchCompleteCallback(null, function(sc, searcher) { sc.setSearchCompleteCallback(null, function (sc, searcher) {
if (searcher.results.length) { if (searcher.results.length) {
var cur = searcher.cursor, var cur = searcher.cursor,
total = parseInt(cur.estimatedResultCount, 10); total = parseInt(cur.estimatedResultCount, 10);
if (total > sc.maxResultCount) { if (total > sc.maxResultCount) {
sc.maxResultCount = total; sc.maxResultCount = total;
$('#cse').show(); $('#cse').show();
window.scroll(0, 0); window.scroll(0, 0);
} }
} else { } else {
$('#resultcount').hide(); $('#resultcount').hide();
$('#no-devsearch-results').show(); $('#no-devsearch-results').show();
}
$(window).resize();
});
$('#cse').hide();
sc.draw('cse', opt);
sc.execute();
if (!qry.val()) {
$('#resultcount').show();
} }
$(window).resize();
});
$('#searchbox').submit(_pd(function(e) { $('#cse').hide();
sc.execute(); sc.draw('cse', opt);
})); sc.execute();
if (!qry.val()) {
$('#resultcount').show();
}
$('#searchbox').submit(
_pd(function (e) {
sc.execute();
}),
);
}, true); }, true);

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -34,30 +34,35 @@ _.template(`
*/ */
/* The following is the above commented template, pre-compiled. */ /* The following is the above commented template, pre-compiled. */
function syntaxhighlighter_template(obj){ function syntaxhighlighter_template(obj) {
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');}; var __t,
with(obj||{}){ __p = '',
__p+='\n <div class="syntaxhighlighter">\n <table border="0" cellpadding="0" cellspacing="0">\n <colgroup><col class="highlighter-column-line-numbers"/>\n <col class="highlighter-column-code"/></colgroup>\n <tbody>\n '; __j = Array.prototype.join,
_.each(lines, function(line) { print = function () {
__p+='\n <tr class="tr-line">\n <td class="td-line-number">\n <a href="#'+ __p += __j.call(arguments, '');
((__t=( line.id ))==null?'':_.escape(__t))+ };
'" id="'+ with (obj || {}) {
((__t=( line.id ))==null?'':_.escape(__t))+ __p +=
'"\n class="'+ '\n <div class="syntaxhighlighter">\n <table border="0" cellpadding="0" cellspacing="0">\n <colgroup><col class="highlighter-column-line-numbers"/>\n <col class="highlighter-column-code"/></colgroup>\n <tbody>\n ';
((__t=( line.class ))==null?'':_.escape(__t))+ _.each(lines, function (line) {
' original line line-number"\n data-linenumber="'+ __p +=
((__t=( line.number ))==null?'':_.escape(__t))+ '\n <tr class="tr-line">\n <td class="td-line-number">\n <a href="#' +
'"></a>\n </td>\n <td class="'+ ((__t = line.id) == null ? '' : _.escape(__t)) +
((__t=( line.class ))==null?'':_.escape(__t))+ '" id="' +
' td-line-code alt'+ ((__t = line.id) == null ? '' : _.escape(__t)) +
((__t=( line.number % 2 + 1))==null?'':_.escape(__t))+ '"\n class="' +
'"><span\n class="original line line-code">'+ ((__t = line.class) == null ? '' : _.escape(__t)) +
((__t=( ' original line line-number"\n data-linenumber="' +
line.code ((__t = line.number) == null ? '' : _.escape(__t)) +
))==null?'':__t)+ '"></a>\n </td>\n <td class="' +
'</span></td>\n </tr>\n '; ((__t = line.class) == null ? '' : _.escape(__t)) +
}) ' td-line-code alt' +
__p+='\n </tbody>\n </table>\n </div>\n'; ((__t = (line.number % 2) + 1) == null ? '' : _.escape(__t)) +
} '"><span\n class="original line line-code">' +
return __p; ((__t = line.code) == null ? '' : __t) +
'</span></td>\n </tr>\n ';
});
__p += '\n </tbody>\n </table>\n </div>\n';
}
return __p;
} }

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,31 +1,33 @@
// CSRF Tokens // CSRF Tokens
// Hijack the AJAX requests, and insert a CSRF token as a header. // Hijack the AJAX requests, and insert a CSRF token as a header.
$(document).ajaxSend(function(event, xhr, ajaxSettings) { $(document)
.ajaxSend(function (event, xhr, ajaxSettings) {
var csrf, $meta; var csrf, $meta;
// Block anything that starts with 'http:', 'https:', '://' or '//'. // Block anything that starts with 'http:', 'https:', '://' or '//'.
if (!/^((https?:)|:?[/]{2})/.test(ajaxSettings.url)) { if (!/^((https?:)|:?[/]{2})/.test(ajaxSettings.url)) {
// Only send the token to relative URLs i.e. locally. // Only send the token to relative URLs i.e. locally.
$meta = $('meta[name=csrf]'); $meta = $('meta[name=csrf]');
if (!z.anonymous && $meta.length) { if (!z.anonymous && $meta.length) {
csrf = $meta.attr('content'); csrf = $meta.attr('content');
} else { } else {
csrf = $("input[name='csrfmiddlewaretoken']").val(); csrf = $("input[name='csrfmiddlewaretoken']").val();
} }
if (csrf) { if (csrf) {
xhr.setRequestHeader('X-CSRFToken', csrf); xhr.setRequestHeader('X-CSRFToken', csrf);
} }
} }
}).ajaxSuccess(function(event, xhr, ajaxSettings) { })
.ajaxSuccess(function (event, xhr, ajaxSettings) {
$(window).trigger('resize'); // Redraw what needs to be redrawn. $(window).trigger('resize'); // Redraw what needs to be redrawn.
}); });
function b64toBlob(data) { function b64toBlob(data) {
var b64str = atob(data); var b64str = atob(data);
var counter = b64str.length; var counter = b64str.length;
var u8arr = new Uint8Array(counter); var u8arr = new Uint8Array(counter);
while(counter--){ while (counter--) {
u8arr[counter] = b64str.charCodeAt(counter); u8arr[counter] = b64str.charCodeAt(counter);
} }
return new Blob([u8arr]); return new Blob([u8arr]);
} }

Просмотреть файл

@ -1,49 +1,51 @@
$(document).ready(function(){ $(document).ready(function () {
if (!$(document.body).hasClass('home')) { if (!$(document.body).hasClass('home')) {
return; return;
}
$('#homepage .listing-header a').click(function (e) {
e.preventDefault();
update(this, true);
});
// Switch to the tab of the <a> given as `link`.
// Only call pushState if `push` is True.
function update(link, push) {
var target = $(link).attr('data-target');
// Change the list to show the right add-ons.
$('.addon-listing').attr('class', 'addon-listing addon-listing-' + target);
// Update the selected tab.
$('.listing-header .selected').removeClass('selected');
$('#' + target)
.addClass('selected')
.focus();
if (push && history.pushState) {
history.pushState({ target: target }, document.title, link.href);
} }
}
$('#homepage .listing-header a').click(function(e) { // If we already have a hash, switch to the tab.
e.preventDefault(); if (location.hash) {
update(this, true); var selected = $('#homepage .listing-header ' + location.hash);
}); if (selected) {
selected.find('a').click().focus();
// Switch to the tab of the <a> given as `link`.
// Only call pushState if `push` is True.
function update(link, push) {
var target = $(link).attr('data-target');
// Change the list to show the right add-ons.
$('.addon-listing').attr('class', 'addon-listing addon-listing-' + target);
// Update the selected tab.
$('.listing-header .selected').removeClass('selected');
$('#' + target).addClass('selected').focus();
if (push && history.pushState) {
history.pushState({target: target}, document.title, link.href);
}
};
// If we already have a hash, switch to the tab.
if (location.hash) {
var selected = $('#homepage .listing-header ' + location.hash);
if (selected) {
selected.find('a').click().focus();
}
} else {
// Add the current page to the history so we can get back.
var selected = $('#homepage .listing-header .selected a')[0];
update(selected, true, true);
} }
} else {
// Add the current page to the history so we can get back.
var selected = $('#homepage .listing-header .selected a')[0];
update(selected, true, true);
}
// Set up our history callback. // Set up our history callback.
$(window).on('popstate', function(ev) { $(window).on('popstate', function (ev) {
// We don't pushState here because we'd be stuck in this position. // We don't pushState here because we'd be stuck in this position.
var e = ev.originalEvent; var e = ev.originalEvent;
if (e.state && e.state.target) { if (e.state && e.state.target) {
var a = $('#' + e.state.target + ' a')[0]; var a = $('#' + e.state.target + ' a')[0];
update(a, false); update(a, false);
} }
}); });
}); });

Просмотреть файл

@ -1,121 +1,133 @@
/* Global initialization script */ /* Global initialization script */
var z = {}; var z = {};
$(document).ready(function(){ $(document).ready(function () {
// Initialize install buttons. // Initialize install buttons.
$('.install').installButton(); $('.install').installButton();
$(window).trigger('buttons_loaded'); $(window).trigger('buttons_loaded');
// Initialize any tabbed interfaces. See: tabs.js // Initialize any tabbed interfaces. See: tabs.js
if ($.fn.tabify) { if ($.fn.tabify) {
$('.tab-wrapper').tabify(); $('.tab-wrapper').tabify();
} }
// Initialize email links // Initialize email links
$('span.emaillink').each(function() { $('span.emaillink').each(function () {
$(this).find('.i').remove(); $(this).find('.i').remove();
var em = $(this).text().split('').reverse().join(''); var em = $(this).text().split('').reverse().join('');
$(this).prev('a').attr('href', 'mailto:' + em).addClass('email'); $(this)
}); .prev('a')
.attr('href', 'mailto:' + em)
.addClass('email');
});
// fake placeholders if we need to. // fake placeholders if we need to.
if (!('placeholder' in document.createElement('input'))) { if (!('placeholder' in document.createElement('input'))) {
$('input[placeholder]').placeholder(); $('input[placeholder]').placeholder();
} }
if (z.readonly) { if (z.readonly) {
$('form[method=post]') $('form[method=post]')
.before(gettext('This feature is temporarily disabled while we perform website maintenance. Please check back a little later.')) .before(
.find('input, button, select').prop('disabled', true).addClass('disabled'); gettext(
} 'This feature is temporarily disabled while we perform website maintenance. Please check back a little later.',
),
)
.find('input, button, select')
.prop('disabled', true)
.addClass('disabled');
}
}); });
z.inlineSVG = (function() { z.inlineSVG = (function () {
var e = document.createElement('div'); var e = document.createElement('div');
e.innerHTML = '<svg></svg>'; e.innerHTML = '<svg></svg>';
return !!(window.SVGSVGElement && e.firstChild instanceof window.SVGSVGElement); return !!(
window.SVGSVGElement && e.firstChild instanceof window.SVGSVGElement
);
})(); })();
if (!z.inlineSVG) { if (!z.inlineSVG) {
$("body").addClass("noInlineSVG"); $('body').addClass('noInlineSVG');
} }
/* prevent-default function wrapper */ /* prevent-default function wrapper */
function _pd(func) { function _pd(func) {
return function(e) { return function (e) {
e.preventDefault(); e.preventDefault();
func.apply(this, arguments); func.apply(this, arguments);
}; };
} }
/* Fake the placeholder attribute since Firefox 3.6 doesn't support it. */ /* Fake the placeholder attribute since Firefox 3.6 doesn't support it. */
jQuery.fn.placeholder = function(new_value) { jQuery.fn.placeholder = function (new_value) {
if (new_value) {
this.attr('placeholder', new_value);
}
if (new_value) { /* Bail early if we have built-in placeholder support. */
this.attr('placeholder', new_value); if ('placeholder' in document.createElement('input')) {
return this;
}
if (new_value && this.hasClass('placeholder')) {
this.val('').blur();
}
return this.focus(function () {
var $this = $(this),
text = $this.attr('placeholder');
if ($this.val() == text) {
$this.val('').removeClass('placeholder');
} }
})
.blur(function () {
var $this = $(this),
text = $this.attr('placeholder');
/* Bail early if we have built-in placeholder support. */ if ($this.val() === '') {
if ('placeholder' in document.createElement('input')) { $this.val(text).addClass('placeholder');
return this; }
} })
.each(function () {
if (new_value && this.hasClass('placeholder')) { /* Remove the placeholder text before submitting the form. */
this.val('').blur(); var self = $(this);
} self.closest('form').submit(function () {
if (self.hasClass('placeholder')) {
return this.focus(function() { self.val('');
var $this = $(this),
text = $this.attr('placeholder');
if ($this.val() == text) {
$this.val('').removeClass('placeholder');
} }
}).blur(function() { });
var $this = $(this), })
text = $this.attr('placeholder'); .blur();
if ($this.val() === '') {
$this.val(text).addClass('placeholder');
}
}).each(function(){
/* Remove the placeholder text before submitting the form. */
var self = $(this);
self.closest('form').submit(function() {
if (self.hasClass('placeholder')) {
self.val('');
}
});
}).blur();
}; };
jQuery.fn.hasattr = function (name) {
jQuery.fn.hasattr = function(name) { return this.attr(name) !== undefined;
return this.attr(name) !== undefined;
}; };
var escape_ = function (s) {
var escape_ = function(s){ if (s === undefined) {
if (s === undefined) { return;
return; }
} return s
return s.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;') .replace(/&/g, '&amp;')
.replace(/'/g, '&#39;').replace(/"/g, '&#34;'); .replace(/>/g, '&gt;')
.replace(/</g, '&lt;')
.replace(/'/g, '&#39;')
.replace(/"/g, '&#34;');
}; };
//TODO(potch): kill underscore dead. until then, fake it on mobile. //TODO(potch): kill underscore dead. until then, fake it on mobile.
if (!('_' in window)) _ = {}; if (!('_' in window)) _ = {};
/* is ``key`` in obj? */ /* is ``key`` in obj? */
_.haskey = function(obj, key) { _.haskey = function (obj, key) {
return typeof obj[key] !== "undefined"; return typeof obj[key] !== 'undefined';
}; };
/* Detect browser, version, and OS. */ /* Detect browser, version, and OS. */
$.extend(z, BrowserUtils()); $.extend(z, BrowserUtils());
$(document.body).addClass(z.platform).toggleClass('badbrowser', z.badBrowser); $(document.body).addClass(z.platform).toggleClass('badbrowser', z.badBrowser);
/* Details for the current application. */ /* Details for the current application. */
z.app = document.body.getAttribute('data-app'); z.app = document.body.getAttribute('data-app');
z.appName = document.body.getAttribute('data-appname'); z.appName = document.body.getAttribute('data-appname');
@ -128,5 +140,5 @@ z.static_url = document.body.getAttribute('data-static-url');
z.readonly = JSON.parse(document.body.getAttribute('data-readonly')); z.readonly = JSON.parse(document.body.getAttribute('data-readonly'));
if (z.badBrowser) { if (z.badBrowser) {
$(".get-fx-message").show(); $('.get-fx-message').show();
} }

Просмотреть файл

@ -1,297 +1,337 @@
// Yes, this is out here for a reason. // Yes, this is out here for a reason.
// We want to hide the non-default locales as fast as possible. // We want to hide the non-default locales as fast as possible.
(function() { (function () {
var dl = $('body').attr("data-default-locale"); var dl = $('body').attr('data-default-locale');
if (dl) { if (dl) {
$(format(".trans>:not([lang='{0}'])", dl)).hide(); $(format(".trans>:not([lang='{0}'])", dl)).hide();
$(format(".trans [lang='{0}']", dl)).show(); $(format(".trans [lang='{0}']", dl)).show();
} }
})(); })();
$(document).ready(function () { $(document).ready(function () {
if (!$("#l10n-menu").length) return; if (!$('#l10n-menu').length) return;
var locales = [], var locales = [],
dl = $('body').attr("data-default-locale"), dl = $('body').attr('data-default-locale'),
currentLocale = dl, currentLocale = dl,
unsavedModalMsg = $('#modal-l10n-unsaved .msg').html(), unsavedModalMsg = $('#modal-l10n-unsaved .msg').html(),
unsavedModal = $('#modal-l10n-unsaved').modal(), unsavedModal = $('#modal-l10n-unsaved').modal(),
rmLocaleModalMsg = $('#modal-l10n-rm .msg').html(), rmLocaleModalMsg = $('#modal-l10n-rm .msg').html(),
rmLocaleModal = $('#modal-l10n-rm').modal(), rmLocaleModal = $('#modal-l10n-rm').modal(),
modalActions = $(".modal-actions", unsavedModal), modalActions = $('.modal-actions', unsavedModal),
translations = {}; //hold the initial values of the fields to check for changes translations = {}; //hold the initial values of the fields to check for changes
$(".primary").on("change keyup paste blur", ".trans input, .trans textarea", checkTranslation); $('.primary').on(
$("form").submit(function () { 'change keyup paste blur',
$(this).find(".trans .cloned").remove(); '.trans input, .trans textarea',
checkTranslation,
);
$('form').submit(function () {
$(this).find('.trans .cloned').remove();
});
function popuplateTranslations(el) {
//load in the initial values of the translations
el.find('.trans input[lang], .trans textarea[lang]').each(function () {
var $input = $(this),
$trans = $input.closest('.trans'),
transKey = $trans.attr('data-name') + '_' + $input.attr('lang');
translations[transKey] = $input.val();
}); });
}
function popuplateTranslations(el) { //load in the initial values of the translations function showExistingLocales() {
el.find(".trans input[lang], .trans textarea[lang]").each(function() { discoverLocales();
var $input = $(this), $el = $('#existing_locales').empty();
$trans = $input.closest(".trans"), $('#all_locales li').show();
transKey = $trans.attr("data-name")+'_'+$input.attr('lang'); $.each(_.without(locales, dl), function () {
translations[transKey] = $input.val(); var locale_row = $(
}); format("#all_locales a[href='#{0}']", [this]),
).parent();
if (locale_row.length) {
$el.append(
format(
"<li><a title='{msg}'class='remove' href='#'>x</a>{row}</li>",
{
msg: gettext('Remove this localization'),
row: locale_row.html(),
},
),
);
locale_row.hide();
}
});
}
function checkTranslation(e, t) {
var $input = e.originalEvent ? $(this) : $(format("[lang='{0}']", [e]), t),
$trans = $input.closest('.trans'),
lang = e.originalEvent ? $input.attr('lang') : e,
$dl = $(format("[lang='{0}']", [dl]), $trans),
transKey = $trans.attr('data-name') + '_' + lang;
if ($input.length == 0 || $input.is('span')) {
// No translation of this element exists for the
// requested language.
return;
} }
if (!(transKey in translations)) {
function showExistingLocales() { translations[transKey] = $input.val();
discoverLocales();
$el = $("#existing_locales").empty();
$("#all_locales li").show();
$.each(_.without(locales, dl), function() {
var locale_row = $(format("#all_locales a[href='#{0}']",[this])).parent();
if (locale_row.length) {
$el.append(format("<li><a title='{msg}'class='remove' href='#'>x</a>{row}</li>",
{ msg: gettext('Remove this localization'),
row: locale_row.html()
}));
locale_row.hide();
}
});
} }
if (lang != dl) {
function checkTranslation(e, t) { if ($input.val() == $dl.val() && $input.val().trim().length) {
var $input = e.originalEvent ? $(this) : $(format("[lang='{0}']", [e]), t), $input.addClass('cloned');
$trans = $input.closest(".trans"), } else if (!$input.val().trim().length) {
lang = e.originalEvent ? $input.attr("lang") : e, if (e.originalEvent && e.type == 'focusout') {
$dl = $(format("[lang='{0}']", [dl]), $trans), $input.val($dl.val()).addClass('cloned');
transKey = $trans.attr("data-name")+'_'+lang;
if ($input.length == 0 || $input.is('span')) {
// No translation of this element exists for the
// requested language.
return;
}
if (!(transKey in translations)) {
translations[transKey] = $input.val();
}
if (lang != dl) {
if ($input.val() == $dl.val() && $input.val().trim().length) {
$input.addClass("cloned");
} else if (!$input.val().trim().length) {
if (e.originalEvent && e.type == "focusout") {
$input.val($dl.val()).addClass("cloned");
} else {
$input.removeClass("cloned");
}
} else {
$input.removeClass("cloned");
}
}
if (translations[transKey] != $input.val()) {
$input.addClass("unsaved");
} else { } else {
$input.removeClass("unsaved"); $input.removeClass('cloned');
} }
} else {
$input.removeClass('cloned');
}
}
if (translations[transKey] != $input.val()) {
$input.addClass('unsaved');
} else {
$input.removeClass('unsaved');
}
}
$('.primary').on('click', '.errorlist .l10n', switchLocale);
$('#all_locales').on('switch', 'a', switchLocale);
// If the locale switcher is visible, use the cookie.
var initLocale = dl;
if ($('#l10n-menu:visible').length) {
initLocale = $.cookie('current_locale');
}
$(format("#all_locales a[href='#{0}']", [initLocale])).trigger('switch');
function switchLocale(e) {
e.preventDefault();
$tgt = $(this);
var new_locale = $tgt.attr('data-lang') || $tgt.attr('href').substring(1);
var unsaved = $('form .trans .unsaved');
if (unsaved.length && new_locale != currentLocale) {
unsavedModal
.children('.msg')
.html(format(unsavedModalMsg, [$('#change-locale').text()]));
unsavedModal.render();
$('#l10n-save-changes')
.off()
.click(function () {
var unsavedForms = $('form:has(.trans .unsaved)');
var numFormsLeft = unsavedForms.length;
var erroredForms = 0;
modalActions.addClass('ajax-loading');
modalActions.find('button').addClass('disabled');
unsavedForms.each(function () {
var $form = $(this);
$.ajax({
url: $form.attr('action'),
type: 'post',
data: $form.serialize(),
error: function () {
modalActions.removeClass('ajax-loading');
},
success: function (d) {
var $resp = $(d);
if (
$form.attr('id') &&
$resp.find('#' + $form.attr('id')).length
) {
$resp = $resp.find('#' + $form.attr('id'));
}
// Add locale names to error messages
annotateLocalizedErrors($resp);
numFormsLeft--;
if ($resp.find('.errorlist').length) {
//display errors if they occur
$form.html($resp.html());
updateLocale();
if (
$resp.find(
format(".errorlist li[data-lang='{0}']", currentLocale),
).length
) {
erroredForms++;
}
} else {
//clean up the errors we inserted
popuplateTranslations($form);
$form.find('.unsaved').removeClass('unsaved');
$form.find('.errorlist').remove();
}
if (numFormsLeft < 1) {
if (erroredForms) {
window.scrollTo(
0,
$('.errorlist .l10n').closest('form').offset().top,
);
$('.errorlist')
.first()
.siblings('.trans')
.find('input:visible, textarea:visible')
.focus();
} else {
updateLocale(new_locale);
}
}
modalActions.removeClass('ajax-loading');
modalActions.find('button').removeClass('disabled');
unsavedModal.hideMe();
},
});
});
});
$('#l10n-discard-changes').click(function () {
$('.trans .unsaved').remove();
updateLocale(new_locale);
unsavedModal.hideMe();
});
$('#l10n-cancel-changes').click(function () {
unsavedModal.hideMe();
});
} else {
updateLocale(new_locale);
} }
$(".primary").on("click", ".errorlist .l10n", switchLocale); if (localePopup) {
localePopup.hideMe();
$("#all_locales").on("switch", "a", switchLocale);
// If the locale switcher is visible, use the cookie.
var initLocale = dl;
if ($('#l10n-menu:visible').length) {
initLocale = $.cookie('current_locale');
} }
$(format("#all_locales a[href='#{0}']",[initLocale])).trigger("switch"); }
function switchLocale(e) { var localePopup = $('#locale-popup').popup('#change-locale', {
pointTo: '#change-locale',
width: 200,
callback: function () {
showExistingLocales();
$('#locale-popup').on('click', 'a:not(.remove)', switchLocale);
$('#locale-popup').on('click', 'a.remove', function (e) {
e.preventDefault(); e.preventDefault();
$tgt = $(this); e.stopPropagation();
var new_locale = $tgt.attr("data-lang") || $tgt.attr("href").substring(1); var toRemove = $(this)
var unsaved = $("form .trans .unsaved"); .closest('li')
.find('a:not(.remove)')
.attr('href')
.substring(1);
rmLocaleModal.children('.msg').html(format(rmLocaleModalMsg, toRemove));
rmLocaleModal.render();
$('#l10n-cancel-rm').off().click(rmLocaleModal.hideMe);
function cleanUp() {
$('.modal-actions', rmLocaleModal).removeClass('ajax-loading');
rmLocaleModal.hideMe();
}
$('#l10n-confirm-rm')
.off()
.click(function (e) {
$('.modal-actions', rmLocaleModal).addClass('ajax-loading');
$.ajax({
url: $('#l10n-menu').attr('data-rm-locale'),
type: 'post',
data: { locale: toRemove },
error: function () {
cleanUp();
},
success: function () {
if (currentLocale == toRemove) {
updateLocale(dl);
}
$('.trans [lang=' + toRemove + ']').remove();
cleanUp();
},
});
});
});
return true;
},
});
if (unsaved.length && new_locale != currentLocale) { function updateLocale(lang) {
unsavedModal.children(".msg") lang = lang || currentLocale;
.html(format(unsavedModalMsg,[$("#change-locale").text()])); if (currentLocale != lang) {
unsavedModal.render(); currentLocale = lang;
$("#l10n-save-changes").off().click(function () { }
var unsavedForms = $('form:has(.trans .unsaved)'); if (!_.include(locales, lang)) {
var numFormsLeft = unsavedForms.length; locales.push(lang);
var erroredForms = 0; }
modalActions.addClass("ajax-loading"); var current = $(format("#locale-popup [href='#{0}']", [lang]))
modalActions.find("button").addClass("disabled"); .first()
unsavedForms.each(function() { .clone();
var $form = $(this); current.find('em').remove();
$.ajax({ $('#change-locale').text(current.text());
url: $form.attr('action'), $('.trans').each(function () {
type: "post", var $el = $(this),
data: $form.serialize(), field = $el.attr('data-name'),
error: function() { label = $(format("label[data-for='{0}']", [field]));
modalActions.removeClass("ajax-loading"); if (!$el.find(format("[lang='{0}']", [lang])).length) {
}, if ($el.children('.trans-init').length) {
success: function(d) { var $ni = $el.children('.trans-init').clone();
var $resp = $(d); $ni.attr({
if ($form.attr('id') && $resp.find('#' + $form.attr('id')).length) { class: '',
$resp = $resp.find('#' + $form.attr('id')); lang: lang,
} id: format('id_{0}_{1}', field, lang),
// Add locale names to error messages name: [field, lang].join('_'),
annotateLocalizedErrors($resp); value: $el.find(format("[lang='{0}']", [dl])).val(),
numFormsLeft--; });
if ($resp.find(".errorlist").length) { //display errors if they occur if (lang != dl) $ni.addClass('cloned');
$form.html($resp.html());
updateLocale();
if ($resp.find(format(".errorlist li[data-lang='{0}']", currentLocale)).length) {
erroredForms++;
}
} else { //clean up the errors we inserted
popuplateTranslations($form);
$form.find(".unsaved").removeClass("unsaved");
$form.find(".errorlist").remove();
}
if (numFormsLeft < 1) {
if (erroredForms) {
window.scrollTo(0,$(".errorlist .l10n").closest("form").offset().top);
$(".errorlist").first().siblings(".trans")
.find("input:visible, textarea:visible").focus();
} else {
updateLocale(new_locale);
}
}
modalActions.removeClass("ajax-loading");
modalActions.find("button").removeClass("disabled");
unsavedModal.hideMe();
}
});
});
});
$("#l10n-discard-changes").click(function () {
$('.trans .unsaved').remove();
updateLocale(new_locale);
unsavedModal.hideMe();
});
$("#l10n-cancel-changes").click(function () {
unsavedModal.hideMe();
});
} else { } else {
updateLocale(new_locale); var $ni = $el.find(format("[lang='{0}']", dl)).clone();
} $ni.attr({
class: 'cloned',
if(localePopup) { lang: lang,
localePopup.hideMe(); });
}
}
var localePopup = $("#locale-popup").popup("#change-locale", {
pointTo: "#change-locale",
width: 200,
callback: function() {
showExistingLocales();
$("#locale-popup").on('click', 'a:not(.remove)', switchLocale);
$("#locale-popup").on('click', 'a.remove', function (e) {
e.preventDefault();
e.stopPropagation();
var toRemove = $(this).closest("li").find("a:not(.remove)").attr("href").substring(1);
rmLocaleModal.children(".msg").html(format(rmLocaleModalMsg,toRemove));
rmLocaleModal.render();
$('#l10n-cancel-rm').off().click(rmLocaleModal.hideMe);
function cleanUp() {
$(".modal-actions", rmLocaleModal).removeClass('ajax-loading');
rmLocaleModal.hideMe();
}
$('#l10n-confirm-rm').off().click(function(e) {
$(".modal-actions", rmLocaleModal).addClass('ajax-loading');
$.ajax({
url: $('#l10n-menu').attr('data-rm-locale'),
type: "post",
data: {locale: toRemove},
error: function() {
cleanUp();
},
success: function() {
if (currentLocale == toRemove) {
updateLocale(dl);
}
$('.trans [lang='+toRemove+']').remove();
cleanUp();
}
});
});
});
return true;
} }
$el.append($ni);
}
checkTranslation(lang, $el);
if (label.length) {
label.children('.locale').remove();
label.append(
format("<span class='locale'>{0}</span>", [
$('#change-locale').text(),
]),
);
label_for = $el.children(format("[lang='{0}']", [lang])).attr('id');
label.attr('for', label_for);
}
}); });
$(format(".trans>:not([lang='{0}'])", currentLocale)).hide();
function updateLocale(lang) { $(format(".trans [lang='{0}']", currentLocale)).show();
lang = lang || currentLocale; initCharCount();
if (currentLocale != lang) { if ($.cookie('current_locale') != currentLocale && currentLocale != dl) {
currentLocale = lang; $.cookie('current_locale', null);
} $.cookie('current_locale', currentLocale, { expires: 0 });
if (!_.include(locales,lang)) {
locales.push(lang);
}
var current = $(format("#locale-popup [href='#{0}']", [lang])).first().clone();
current.find('em').remove();
$("#change-locale").text(current.text());
$(".trans").each(function () {
var $el = $(this),
field = $el.attr('data-name'),
label = $(format("label[data-for='{0}']",[field]));
if (!$el.find(format("[lang='{0}']",[lang])).length) {
if ($el.children(".trans-init").length) {
var $ni = $el.children(".trans-init").clone();
$ni.attr({
"class": "",
lang: lang,
id: format('id_{0}_{1}', field, lang),
name: [field,lang].join('_'),
value: $el.find(format("[lang='{0}']",[dl])).val()
});
if (lang != dl) $ni.addClass("cloned");
} else {
var $ni = $el.find(format("[lang='{0}']",dl)).clone();
$ni.attr({
"class": "cloned",
lang: lang
});
}
$el.append($ni);
}
checkTranslation(lang, $el);
if (label.length) {
label.children(".locale").remove();
label.append(format("<span class='locale'>{0}</span>",[$("#change-locale").text()]));
label_for = $el.children(format("[lang='{0}']",[lang])).attr('id');
label.attr('for', label_for);
}
});
$(format(".trans>:not([lang='{0}'])", currentLocale)).hide();
$(format(".trans [lang='{0}']", currentLocale)).show();
initCharCount();
if ($.cookie('current_locale') != currentLocale &&
currentLocale != dl) {
$.cookie('current_locale', null);
$.cookie('current_locale', currentLocale, {expires: 0});
}
} }
}
function discoverLocales(locale) { function discoverLocales(locale) {
var seen_locales = {}; var seen_locales = {};
$(".trans [lang]").each(function () { $('.trans [lang]').each(function () {
seen_locales[$(this).attr('lang')] = true; seen_locales[$(this).attr('lang')] = true;
}); });
locales = _.keys(seen_locales); locales = _.keys(seen_locales);
} }
z.refreshL10n = function(lang) { z.refreshL10n = function (lang) {
updateLocale(lang); updateLocale(lang);
}; };
updateLocale(); updateLocale();
}); });
function annotateLocalizedErrors($el) { function annotateLocalizedErrors($el) {
$el.find(".errorlist li[data-lang]:not(.l10n)").each(function() { $el.find('.errorlist li[data-lang]:not(.l10n)').each(function () {
var err = $(this), var err = $(this),
t = err.text(), t = err.text(),
l = $(format("#locale-popup [href='#{0}']", [err.attr('data-lang')])).first().text(); l = $(format("#locale-popup [href='#{0}']", [err.attr('data-lang')]))
err.text(format("{0}: ",[l])+t).addClass("l10n"); .first()
}); .text();
err.text(format('{0}: ', [l]) + t).addClass('l10n');
});
} }
function loc(s) { function loc(s) {
// A noop function for strings that are not ready to be localized. // A noop function for strings that are not ready to be localized.
return s; return s;
} }

Просмотреть файл

@ -1,111 +1,114 @@
$(document).ready(function() { $(document).ready(function () {
var report = $('.review-reason').html(); var report = $('.review-reason').html();
$(".review-reason").popup(".flag-review", { $('.review-reason').popup('.flag-review', {
delegate: $(document.body), delegate: $(document.body),
width: 'inherit', width: 'inherit',
callback: function(obj) { callback: function (obj) {
var ct = $(obj.click_target), var ct = $(obj.click_target),
$popup = this; $popup = this;
function addFlag(flag, note) { function addFlag(flag, note) {
$.ajax({type: 'POST', $.ajax({
url: ct.attr("href"), type: 'POST',
data: {flag: flag, note: note}, url: ct.attr('href'),
success: function() { data: { flag: flag, note: note },
$popup.removeClass("other") success: function () {
.hideMe(); $popup.removeClass('other').hideMe();
ct.replaceWith(gettext('Flagged for review')); ct.replaceWith(gettext('Flagged for review'));
}, },
error: function(){ }, error: function () {},
dataType: 'json' dataType: 'json',
}); });
}; }
$popup.on("click", "li a", function(e) { $popup.on('click', 'li a', function (e) {
e.preventDefault();
var el = $(e.target);
if (el.attr("href") == "#review_flag_reason_other") {
$popup.addClass('other')
.on("submit", "form", function(e) {
e.preventDefault();
var note = $popup.find('#id_note').val();
if (!note) {
alert(gettext('Your input is required'));
} else {
addFlag('review_flag_reason_other', note);
}
})
.setPos(ct)
.find('input[type=text]')
.focus();
} else {
addFlag(el.attr("href").slice(1));
}
});
$popup.html(report);
return { pointTo: ct };
}
});
$('.primary').on('click', '.review-edit', function(e) {
e.preventDefault(); e.preventDefault();
var $form = $("#review-edit-form"), var el = $(e.target);
$review = $(this).parents(".review"), if (el.attr('href') == '#review_flag_reason_other') {
rating = $review.attr("data-rating"), $popup
edit_url = $("a.permalink", $review).attr("href") + "edit"; .addClass('other')
$cancel = $("#review-edit-cancel"); .on('submit', 'form', function (e) {
e.preventDefault();
$review.attr("action", edit_url); var note = $popup.find('#id_note').val();
$form.detach().insertAfter($review); if (!note) {
$("#id_title").val($review.children("h5").text()); alert(gettext('Your input is required'));
$(".ratingwidget input:radio[value=" + rating + "]", $form).click(); } else {
$("#id_body").val($review.children("p.review-body").text()); addFlag('review_flag_reason_other', note);
$review.hide(); }
$form.show(); })
.setPos(ct)
function done_edit() { .find('input[type=text]')
$form.off().hide(); .focus();
$review.show(); } else {
$cancel.off(); addFlag(el.attr('href').slice(1));
} }
});
$cancel.click(function(e) { $popup.html(report);
e.preventDefault(); return { pointTo: ct };
done_edit(); },
}); });
$form.submit(function (e) { $('.primary').on('click', '.review-edit', function (e) {
e.preventDefault(); e.preventDefault();
$.ajax({type: 'POST', var $form = $('#review-edit-form'),
url: edit_url, $review = $(this).parents('.review'),
data: $form.serialize(), rating = $review.attr('data-rating'),
success: function(response, status) { edit_url = $('a.permalink', $review).attr('href') + 'edit';
$review.children("h5").text($("#id_title").val()); $cancel = $('#review-edit-cancel');
rating = $(".ratingwidget input:radio:checked", $form).val();
$(".stars", $review).removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5').addClass('stars-' + rating); $review.attr('action', edit_url);
rating = $review.attr("data-rating", rating); $form.detach().insertAfter($review);
$review.children("p.review-body").text($("#id_body").val()); $('#id_title').val($review.children('h5').text());
done_edit(); $('.ratingwidget input:radio[value=' + rating + ']', $form).click();
}, $('#id_body').val($review.children('p.review-body').text());
dataType: 'json' $review.hide();
}); $form.show();
return false;
}); function done_edit() {
$form.off().hide();
$review.show();
$cancel.off();
}
$cancel.click(function (e) {
e.preventDefault();
done_edit();
}); });
$form.submit(function (e) {
$('.delete-review').click(function(e) { e.preventDefault();
e.preventDefault(); $.ajax({
var target = $(e.target); type: 'POST',
$.post(target.attr('href'), function() { url: edit_url,
target.replaceWith(gettext('Marked for deletion')); data: $form.serialize(),
}); success: function (response, status) {
target.closest('.review').addClass('deleted'); $review.children('h5').text($('#id_title').val());
rating = $('.ratingwidget input:radio:checked', $form).val();
$('.stars', $review)
.removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5')
.addClass('stars-' + rating);
rating = $review.attr('data-rating', rating);
$review.children('p.review-body').text($('#id_body').val());
done_edit();
},
dataType: 'json',
});
return false;
}); });
});
$("select[name='rating']").ratingwidget(); $('.delete-review').click(function (e) {
e.preventDefault();
var target = $(e.target);
$.post(target.attr('href'), function () {
target.replaceWith(gettext('Marked for deletion'));
});
target.closest('.review').addClass('deleted');
});
$('.review-flagged.disabled input:not([type=hidden])').prop('disabled', true); $("select[name='rating']").ratingwidget();
$('.review-flagged.disabled input:not([type=hidden])').prop('disabled', true);
}); });

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,251 +1,297 @@
$(document).ready(function() { $(document).ready(function () {
$('#theme-wizard').each(initThemeWizard);
$('#theme-wizard').each(initThemeWizard); var MAX_STATICTHEME_SIZE = 7 * 1024 * 1024;
var MAX_STATICTHEME_SIZE = 7 * 1024 * 1024; function initThemeWizard() {
var $wizard = $(this);
var preLoadBlob = null;
var headerImageError = false;
function initThemeWizard() { function getFile() {
var $wizard = $(this); file_selector = $wizard.find('#header-img')[0];
var preLoadBlob = null; file = file_selector.files[0];
var headerImageError = false; if (
file &&
function getFile() { $wizard
file_selector = $wizard.find('#header-img')[0]; .find('#header-img')
file = file_selector.files[0]; .attr('accept')
if (file && $wizard.find('#header-img').attr('accept').split(',').indexOf(file.type) == -1) .split(',')
return null; .indexOf(file.type) == -1
return file ? file : preLoadBlob; )
} return null;
return file ? file : preLoadBlob;
$wizard.on('click', '.reset', _pd(function() {
var $this = $(this),
$row = $this.closest('.row');
$row.find('input[type="file"]').click();
}));
$wizard.on('change', 'input[type="file"]', function() {
var $row = $(this).closest('.row');
var reader = new FileReader(),
file = getFile();
if (!file) return; // don't do anything if no file selected.
var $preview_img = $row.find('.preview');
reader.onload = function(e) {
$preview_img.attr('src', e.target.result);
$preview_img.show().addClass('loaded');
$row.find('.reset').show().css('display', 'block');
$row.find('input[type=file], .note').hide();
var filename = file.name.replace(/\.[^/.]+$/, "");
$wizard.find('a.download').attr('download', filename + ".zip");
var name_input = $wizard.find('#theme-name');
if (!name_input.val()) {
name_input.val(filename);
}
updateManifest();
};
reader.readAsDataURL(file);
});
$wizard.find('input[type="file"]').trigger('change');
$wizard.find('img.preview').on('load', function(e) {
var $svg_img = $('#svg-header-img'),
$svg = $('#preview-svg-root');
$svg_img.attr('href', ($svg_img.src = e.target.src));
$svg_img.attr('height', e.target.naturalHeight);
var meetOrSlice = (e.target.naturalWidth < $svg.width())? 'meet' : 'slice';
$svg_img.attr('preserveAspectRatio', 'xMaxYMin '+ meetOrSlice);
});
$wizard.find('#theme-header').each(function(index, element) {
var img_src = $(element).data('existing-header');
// If we already have a preview from a selected file don't overwrite it.
if (getFile() || !img_src) return;
var xhr = new XMLHttpRequest();
xhr.open("GET", window.location.href + "/background");
xhr.responseType = "json";
// load the image as a blob so we can treat it as a File
xhr.onload = function() {
jsonResponse = xhr.response;
preLoadBlob = b64toBlob(jsonResponse[img_src]);
preLoadBlob.name = img_src;
$wizard.find('input[type="file"]').trigger('change');
};
xhr.send();
});
function updateManifest() {
textarea = $wizard.find('#manifest').val(generateManifest());
toggleSubmitIfNeeded();
}
function toggleSubmitIfNeeded() {
$wizard.find('button.upload').attr('disabled', ! required_fields_present());
}
function generateManifest() {
var headerFile = getFile(),
headerPath = headerFile ? headerFile.name : "";
function colVal(id) {
return $wizard.find('#' + id).val();
}
var colors = {
"frame": colVal('frame'),
"tab_background_text": colVal('tab_background_text'),
"toolbar": colVal('toolbar'),
"bookmark_text": colVal('bookmark_text'),
"toolbar_field": colVal('toolbar_field'),
"toolbar_field_text": colVal('toolbar_field_text')
};
colors = _.omit(colors, function(value) {return value === "";});
manifest = {
name: $wizard.find('#theme-name').val(),
manifest_version: 2,
version: $wizard.data('version'),
theme: {
images: {
theme_frame: headerPath
},
colors: colors
}
};
return JSON.stringify(manifest, null, 4);
}
function buildZip() {
var zip = new JSZip();
zip.file('manifest.json', generateManifest());
var header_img = getFile();
if (header_img) {
zip.file(header_img.name, header_img);
}
return zip;
}
var $color = $wizard.find('input.color-picker');
$color.change(function() {
var $this = $(this),
color_property_selector = '.' + $this[0].id,
$svg_element = $(color_property_selector),
// If there's no value set and we have a fallback color we can use that instead
$have_fallback = $(color_property_selector + '[data-fallback]').not('[data-fallback=' + $this[0].id + ']');
if (!$this.val()) {
$svg_element.attr('fill', $svg_element.data('fill'));
$have_fallback.attr('fill', $('#' + $svg_element.data('fallback')).val())
.addClass($svg_element.data('fallback'));
} else {
$have_fallback.removeClass($svg_element.data('fallback'));
$svg_element.attr('fill', $this.val());
}
updateManifest();
}).trigger('change');
$color.minicolors({
dataUris: true,
opacity: true,
format: 'rgb',
change: function() {
$color.trigger('change');
updateManifest();
}
});
/* Force the pop-up panel ltr or the images end up in the wrong
position. */
$wizard.find('div.minicolors-panel').attr('dir', 'ltr');
/* The submit button availability needs to follow changes to the theme
name as soon as they happen, to react properly if it's modified but
the user hasn't focused something else yet */
$wizard.on('input', '#theme-name', toggleSubmitIfNeeded);
/* We update the full manifest when a proper change event is triggered,
the user has finished editing the name at this point. */
$wizard.on('change', '#theme-name', updateManifest);
$wizard.on('click', 'button.upload', _pd(function(event) {
var $button = $(event.target);
var zip = buildZip();
$button.addClass('uploading').addClass('disabled')
.data('upload-text', $button.text())
.text($button.data('uploading-text'));
zip.generateAsync({type: 'blob'}).then(function (blob) {
if (blob.size > MAX_STATICTHEME_SIZE) {
headerImageError = true;
throw format(gettext("Maximum upload size is {0} - choose a smaller background image."), fileSizeFormat(MAX_STATICTHEME_SIZE));
}
return blob;
}).then(function (blob) {
var formData = new FormData();
formData.append('upload', blob, 'upload.zip');
$.ajax({
type: 'POST',
url: $button.attr('formaction'),
data: formData,
processData: false,
contentType: false
}).done(function (data){
$('#id_upload').val(data.upload);
uploadDone(data);
});
}, function (err) {
// Fake the validation so we can display as an error.
uploadDone({validation:{
errors:1,
messages:[{message:err}]
}});
});
}));
function uploadDone(data) {
if (!data.validation) {
setTimeout(function() {
$.ajax({
url: data.url,
dataType: 'json',
success: uploadDone,
error: function (xhr, text, error) {
if (xhr.responseJSON && xhr.responseJSON.validation) {
// even though we got an error response code, it's validation json.
data = xhr.responseJSON;
} else {
// Fake the validation so we can display as an error.
data = {
validation:{
errors:1,
messages:[{message:error}]
}};
}
uploadDone(data);
}
});
}, 1000);
} else {
if (data.validation.errors === 0 ) {
$wizard.find('#submit-describe').submit();
} else {
data.validation.messages.forEach(function(message) {
if (headerImageError) {
$('.header-image-error').append($('<li>', {'html': message.message}));
} else {
$('.general-validation-error').append($('<li>', {'html': message.message}));
}
console.error(message);
});
$('button.upload').removeClass('uploading').removeClass('disabled')
.text($('button.upload').data('upload-text'));
headerImageError = false;
}
}
}
function required_fields_present() {
return $wizard.find('#theme-name').val() !== "" &&
getFile() &&
$wizard.find('#frame').val() !== "" &&
$wizard.find('#tab_background_text').val() !== "";
}
} }
$wizard.on(
'click',
'.reset',
_pd(function () {
var $this = $(this),
$row = $this.closest('.row');
$row.find('input[type="file"]').click();
}),
);
$wizard.on('change', 'input[type="file"]', function () {
var $row = $(this).closest('.row');
var reader = new FileReader(),
file = getFile();
if (!file) return; // don't do anything if no file selected.
var $preview_img = $row.find('.preview');
reader.onload = function (e) {
$preview_img.attr('src', e.target.result);
$preview_img.show().addClass('loaded');
$row.find('.reset').show().css('display', 'block');
$row.find('input[type=file], .note').hide();
var filename = file.name.replace(/\.[^/.]+$/, '');
$wizard.find('a.download').attr('download', filename + '.zip');
var name_input = $wizard.find('#theme-name');
if (!name_input.val()) {
name_input.val(filename);
}
updateManifest();
};
reader.readAsDataURL(file);
});
$wizard.find('input[type="file"]').trigger('change');
$wizard.find('img.preview').on('load', function (e) {
var $svg_img = $('#svg-header-img'),
$svg = $('#preview-svg-root');
$svg_img.attr('href', ($svg_img.src = e.target.src));
$svg_img.attr('height', e.target.naturalHeight);
var meetOrSlice = e.target.naturalWidth < $svg.width() ? 'meet' : 'slice';
$svg_img.attr('preserveAspectRatio', 'xMaxYMin ' + meetOrSlice);
});
$wizard.find('#theme-header').each(function (index, element) {
var img_src = $(element).data('existing-header');
// If we already have a preview from a selected file don't overwrite it.
if (getFile() || !img_src) return;
var xhr = new XMLHttpRequest();
xhr.open('GET', window.location.href + '/background');
xhr.responseType = 'json';
// load the image as a blob so we can treat it as a File
xhr.onload = function () {
jsonResponse = xhr.response;
preLoadBlob = b64toBlob(jsonResponse[img_src]);
preLoadBlob.name = img_src;
$wizard.find('input[type="file"]').trigger('change');
};
xhr.send();
});
function updateManifest() {
textarea = $wizard.find('#manifest').val(generateManifest());
toggleSubmitIfNeeded();
}
function toggleSubmitIfNeeded() {
$wizard
.find('button.upload')
.attr('disabled', !required_fields_present());
}
function generateManifest() {
var headerFile = getFile(),
headerPath = headerFile ? headerFile.name : '';
function colVal(id) {
return $wizard.find('#' + id).val();
}
var colors = {
frame: colVal('frame'),
tab_background_text: colVal('tab_background_text'),
toolbar: colVal('toolbar'),
bookmark_text: colVal('bookmark_text'),
toolbar_field: colVal('toolbar_field'),
toolbar_field_text: colVal('toolbar_field_text'),
};
colors = _.omit(colors, function (value) {
return value === '';
});
manifest = {
name: $wizard.find('#theme-name').val(),
manifest_version: 2,
version: $wizard.data('version'),
theme: {
images: {
theme_frame: headerPath,
},
colors: colors,
},
};
return JSON.stringify(manifest, null, 4);
}
function buildZip() {
var zip = new JSZip();
zip.file('manifest.json', generateManifest());
var header_img = getFile();
if (header_img) {
zip.file(header_img.name, header_img);
}
return zip;
}
var $color = $wizard.find('input.color-picker');
$color
.change(function () {
var $this = $(this),
color_property_selector = '.' + $this[0].id,
$svg_element = $(color_property_selector),
// If there's no value set and we have a fallback color we can use that instead
$have_fallback = $(color_property_selector + '[data-fallback]').not(
'[data-fallback=' + $this[0].id + ']',
);
if (!$this.val()) {
$svg_element.attr('fill', $svg_element.data('fill'));
$have_fallback
.attr('fill', $('#' + $svg_element.data('fallback')).val())
.addClass($svg_element.data('fallback'));
} else {
$have_fallback.removeClass($svg_element.data('fallback'));
$svg_element.attr('fill', $this.val());
}
updateManifest();
})
.trigger('change');
$color.minicolors({
dataUris: true,
opacity: true,
format: 'rgb',
change: function () {
$color.trigger('change');
updateManifest();
},
});
/* Force the pop-up panel ltr or the images end up in the wrong
position. */
$wizard.find('div.minicolors-panel').attr('dir', 'ltr');
/* The submit button availability needs to follow changes to the theme
name as soon as they happen, to react properly if it's modified but
the user hasn't focused something else yet */
$wizard.on('input', '#theme-name', toggleSubmitIfNeeded);
/* We update the full manifest when a proper change event is triggered,
the user has finished editing the name at this point. */
$wizard.on('change', '#theme-name', updateManifest);
$wizard.on(
'click',
'button.upload',
_pd(function (event) {
var $button = $(event.target);
var zip = buildZip();
$button
.addClass('uploading')
.addClass('disabled')
.data('upload-text', $button.text())
.text($button.data('uploading-text'));
zip
.generateAsync({ type: 'blob' })
.then(function (blob) {
if (blob.size > MAX_STATICTHEME_SIZE) {
headerImageError = true;
throw format(
gettext(
'Maximum upload size is {0} - choose a smaller background image.',
),
fileSizeFormat(MAX_STATICTHEME_SIZE),
);
}
return blob;
})
.then(
function (blob) {
var formData = new FormData();
formData.append('upload', blob, 'upload.zip');
$.ajax({
type: 'POST',
url: $button.attr('formaction'),
data: formData,
processData: false,
contentType: false,
}).done(function (data) {
$('#id_upload').val(data.upload);
uploadDone(data);
});
},
function (err) {
// Fake the validation so we can display as an error.
uploadDone({
validation: {
errors: 1,
messages: [{ message: err }],
},
});
},
);
}),
);
function uploadDone(data) {
if (!data.validation) {
setTimeout(function () {
$.ajax({
url: data.url,
dataType: 'json',
success: uploadDone,
error: function (xhr, text, error) {
if (xhr.responseJSON && xhr.responseJSON.validation) {
// even though we got an error response code, it's validation json.
data = xhr.responseJSON;
} else {
// Fake the validation so we can display as an error.
data = {
validation: {
errors: 1,
messages: [{ message: error }],
},
};
}
uploadDone(data);
},
});
}, 1000);
} else {
if (data.validation.errors === 0) {
$wizard.find('#submit-describe').submit();
} else {
data.validation.messages.forEach(function (message) {
if (headerImageError) {
$('.header-image-error').append(
$('<li>', { html: message.message }),
);
} else {
$('.general-validation-error').append(
$('<li>', { html: message.message }),
);
}
console.error(message);
});
$('button.upload')
.removeClass('uploading')
.removeClass('disabled')
.text($('button.upload').data('upload-text'));
headerImageError = false;
}
}
}
function required_fields_present() {
return (
$wizard.find('#theme-name').val() !== '' &&
getFile() &&
$wizard.find('#frame').val() !== '' &&
$wizard.find('#tab_background_text').val() !== ''
);
}
}
}); });

Просмотреть файл

@ -15,61 +15,61 @@
* *
* Requires jQuery and jQuery Cookie plugin. * Requires jQuery and jQuery Cookie plugin.
*/ */
z.Storage = (function() { z.Storage = (function () {
var cookieStorage = { var cookieStorage = {
expires: 30, expires: 30,
getItem: function(key) { getItem: function (key) {
return $.cookie(key); return $.cookie(key);
}, },
setItem: function(key, value) { setItem: function (key, value) {
return $.cookie(key, value, {path: '/', expires: this.expires}); return $.cookie(key, value, { path: '/', expires: this.expires });
}, },
removeItem: function(key) { removeItem: function (key) {
return $.cookie(key, null); return $.cookie(key, null);
} },
}; };
var engine = z.capabilities.localStorage ? localStorage : cookieStorage; var engine = z.capabilities.localStorage ? localStorage : cookieStorage;
return function(namespace) { return function (namespace) {
namespace = namespace ? namespace + '-' : ''; namespace = namespace ? namespace + '-' : '';
return { return {
get: function(key) { get: function (key) {
return engine.getItem(namespace + key); return engine.getItem(namespace + key);
}, },
set: function(key, value) { set: function (key, value) {
return engine.setItem(namespace + key, value); return engine.setItem(namespace + key, value);
}, },
remove: function(key) { remove: function (key) {
return engine.removeItem(namespace + key); return engine.removeItem(namespace + key);
} },
};
}; };
};
})(); })();
z.SessionStorage = (function() { z.SessionStorage = (function () {
var cookieStorage = { var cookieStorage = {
getItem: function(key) { getItem: function (key) {
return $.cookie(key); return $.cookie(key);
}, },
setItem: function(key, value) { setItem: function (key, value) {
return $.cookie(key, value, {path: '/'}); return $.cookie(key, value, { path: '/' });
}, },
removeItem: function(key) { removeItem: function (key) {
return $.cookie(key, null); return $.cookie(key, null);
} },
}; };
var engine = z.capabilities.localStorage ? sessionStorage : cookieStorage; var engine = z.capabilities.localStorage ? sessionStorage : cookieStorage;
return function(namespace) { return function (namespace) {
namespace = namespace ? namespace + '-' : ''; namespace = namespace ? namespace + '-' : '';
return { return {
get: function(key) { get: function (key) {
return engine.getItem(namespace + key); return engine.getItem(namespace + key);
}, },
set: function(key, value) { set: function (key, value) {
return engine.setItem(namespace + key, value); return engine.setItem(namespace + key, value);
}, },
remove: function(key) { remove: function (key) {
return engine.removeItem(namespace + key); return engine.removeItem(namespace + key);
} },
};
}; };
};
})(); })();

Просмотреть файл

@ -7,131 +7,131 @@
* .tabify() plugin so it can be accessed later. * .tabify() plugin so it can be accessed later.
*/ */
var Tabs = function(el) { var Tabs = function (el) {
this.root = $(el); this.root = $(el);
this.init(); this.init();
}; };
Tabs.prototype = { Tabs.prototype = {
init: function() { init: function () {
this.root.addClass('tab-wrapper'); this.root.addClass('tab-wrapper');
this.tabMap = {}; this.tabMap = {};
this.panelMap = {}; this.panelMap = {};
this.reset(); this.reset();
this.select(); this.select();
/* Bind hashchange, trigger event to check for existing hash. */ /* Bind hashchange, trigger event to check for existing hash. */
var self = this; var self = this;
$(document).on('hashchange', function(e) { $(document)
self.hashChange(e); .on('hashchange', function (e) {
}).trigger('hashchange'); self.hashChange(e);
}, })
.trigger('hashchange');
},
/* Find and prepare all the tabs and panels. Can be called multiple times, /* Find and prepare all the tabs and panels. Can be called multiple times,
* e.g. to update tabs after insertion/deletion. * e.g. to update tabs after insertion/deletion.
*/ */
reset: function(o) { reset: function (o) {
this.findTabs(); this.findTabs();
this.findPanels(); this.findPanels();
this.styleTabs(this.tabs); this.styleTabs(this.tabs);
this.stylePanels(this.panels); this.stylePanels(this.panels);
return this;
},
/* Find tabs (li a[href]) and bind their click event. */
findTabs: function() {
this.list = this.root.find('ol,ul').eq(0);
this.tabs = $('li:has(a[href])', this.list);
var self = this;
var cb = function(e) {
e.preventDefault();
self.select($(e.target).attr('href'), true);
$("a", this).blur();
};
this.tabs.off('click', cb).click(cb);
},
/* Get the fragment this tab points to. */
getHash: function(tab) {
return $(tab).find('a').attr('href');
},
/* Find all the panels to go along with the tabs. */
findPanels: function() {
var self = this;
var panels = [];
this.tabs.each(function() {
var hash = self.getHash(this);
var panel = self.root.find(hash)[0];
if (panel) {
self.tabMap[hash] = this;
self.panelMap[hash] = panel;
panels.push(panel);
}
});
this.panels = $(panels);
},
styleTabs: function(tabs) {
tabs = tabs || self.tabs;
this.list.addClass('tab-nav');
$(tabs).addClass('tab');
},
stylePanels: function(panels) {
panels = panels || self.panels;
$(panels).addClass('tab-panel');
},
/* Focus on the tab pointing to #hash.
* If hash is not given, the first tab will be selected.
* If updateHash is true, location.hash will be updated.
*/
select: function(hash, updateHash) {
if (typeof hash === 'undefined') {
if (!this.tabs.filter('.tab-selected').length) {
return this.select(this.getHash(this.tabs[0]));
}
}
var tab = this.tabMap[hash],
panel = this.panelMap[hash];
this.tabs.filter('.tab-selected').removeClass('tab-selected');
this.panels.filter('.tab-selected').removeClass('tab-selected');
$([tab, panel]).addClass('tab-selected');
this.root.trigger('tabselect', {tab: tab, panel: panel});
if (updateHash) {
safeHashChange(hash);
}
},
/* Handler for onhashchange. */
hashChange: function(e) {
if (location.hash && _.haskey(this.tabMap, location.hash)) {
e.preventDefault();
this.select(location.hash);
}
}
};
$.fn.tabify = function() {
this.each(function() {
this.tab = new Tabs(this);
});
return this; return this;
},
/* Find tabs (li a[href]) and bind their click event. */
findTabs: function () {
this.list = this.root.find('ol,ul').eq(0);
this.tabs = $('li:has(a[href])', this.list);
var self = this;
var cb = function (e) {
e.preventDefault();
self.select($(e.target).attr('href'), true);
$('a', this).blur();
};
this.tabs.off('click', cb).click(cb);
},
/* Get the fragment this tab points to. */
getHash: function (tab) {
return $(tab).find('a').attr('href');
},
/* Find all the panels to go along with the tabs. */
findPanels: function () {
var self = this;
var panels = [];
this.tabs.each(function () {
var hash = self.getHash(this);
var panel = self.root.find(hash)[0];
if (panel) {
self.tabMap[hash] = this;
self.panelMap[hash] = panel;
panels.push(panel);
}
});
this.panels = $(panels);
},
styleTabs: function (tabs) {
tabs = tabs || self.tabs;
this.list.addClass('tab-nav');
$(tabs).addClass('tab');
},
stylePanels: function (panels) {
panels = panels || self.panels;
$(panels).addClass('tab-panel');
},
/* Focus on the tab pointing to #hash.
* If hash is not given, the first tab will be selected.
* If updateHash is true, location.hash will be updated.
*/
select: function (hash, updateHash) {
if (typeof hash === 'undefined') {
if (!this.tabs.filter('.tab-selected').length) {
return this.select(this.getHash(this.tabs[0]));
}
}
var tab = this.tabMap[hash],
panel = this.panelMap[hash];
this.tabs.filter('.tab-selected').removeClass('tab-selected');
this.panels.filter('.tab-selected').removeClass('tab-selected');
$([tab, panel]).addClass('tab-selected');
this.root.trigger('tabselect', { tab: tab, panel: panel });
if (updateHash) {
safeHashChange(hash);
}
},
/* Handler for onhashchange. */
hashChange: function (e) {
if (location.hash && _.haskey(this.tabMap, location.hash)) {
e.preventDefault();
this.select(location.hash);
}
},
}; };
$.fn.tabify = function () {
this.each(function () {
this.tab = new Tabs(this);
});
return this;
};
/* Change location.hash without scrolling. */ /* Change location.hash without scrolling. */
var safeHashChange = function(hash) { var safeHashChange = function (hash) {
var el = $(hash); var el = $(hash);
el.attr('id', ''); el.attr('id', '');
location.hash = hash; location.hash = hash;
el.attr('id', hash.slice(1)); el.attr('id', hash.slice(1));
}; };

Просмотреть файл

@ -1,22 +1,31 @@
$(document).ready(function(){ $(document).ready(function () {
if (!z.appMatchesUserAgent) { if (!z.appMatchesUserAgent) {
return; return;
}
var themeCompat = function () {
var $el = $(this),
min = $el.attr('data-min'),
max = $el.attr('data-max');
if (
VersionCompare.compareVersions(z.browserVersion, min) < 0 ||
VersionCompare.compareVersions(z.browserVersion, max) > 0
) {
$el.addClass('incompatible');
var msg = format(
gettext('This theme is incompatible with your version of {0}'),
[z.appName],
);
$el.append('<div class="overlay"><p>' + msg + '</p></div>').hover(
function () {
$(this).children('.overlay').show();
},
function () {
$(this).children('.overlay').fadeOut();
},
);
} }
};
var themeCompat = function() { $('.browse-thumbs .thumbs li').each(themeCompat);
var $el = $(this),
min = $el.attr('data-min'),
max = $el.attr('data-max');
if (VersionCompare.compareVersions(z.browserVersion, min) < 0
|| VersionCompare.compareVersions(z.browserVersion, max) > 0) {
$el.addClass('incompatible');
var msg = format(gettext('This theme is incompatible with your version of {0}'),
[z.appName]);
$el.append('<div class="overlay"><p>' + msg + '</p></div>')
.hover(function() { $(this).children('.overlay').show(); },
function() { $(this).children('.overlay').fadeOut(); });
}
};
$('.browse-thumbs .thumbs li').each(themeCompat);
}); });

Просмотреть файл

@ -1,470 +1,529 @@
(function($) { (function ($) {
/* jQuery.ScrollTo by Ariel Flesler */ /* jQuery.ScrollTo by Ariel Flesler */
$.fn.scrollTo = function(opts) { $.fn.scrollTo = function (opts) {
if (!this.length) return this; if (!this.length) return this;
opts = $.extend({ opts = $.extend(
duration: 250, {
marginTop: 0, duration: 250,
complete: undefined marginTop: 0,
}, opts || { }); complete: undefined,
var top = this.offset().top - opts.marginTop; },
$('html, body').animate({ 'scrollTop': top }, opts.duration, undefined, opts.complete); opts || {},
return this; );
}; var top = this.offset().top - opts.marginTop;
$('html, body').animate(
{ scrollTop: top },
opts.duration,
undefined,
opts.complete,
);
return this;
};
})(jQuery); })(jQuery);
(function ($) {
var win = $(window);
var doc = $(document);
(function($) { $.fn.themeQueue = function () {
var win = $(window); return this.each(function () {
var doc = $(document); var queue = this;
var currentTheme = 0;
var cacheQueueHeight;
var $queueContext = $('.queue-context');
var actionConstants = $queueContext.data('actions');
$.fn.themeQueue = function() { var themesList = $('div.theme', queue);
return this.each(function() { var themes = themesList
var queue = this; .map(function () {
var currentTheme = 0; return {
var cacheQueueHeight; element: this,
top: 0,
};
})
.get();
var $queueContext = $('.queue-context'); function nthTheme(i) {
var actionConstants = $queueContext.data('actions'); return themesList[i];
}
var themesList = $('div.theme', queue); doc.scroll(
var themes = themesList.map(function() { _.throttle(function () {
return { updateMetrics();
element: this, var i = findCurrentTheme();
top: 0 if (i >= 0 && i != currentTheme) {
};
}).get();
function nthTheme(i) {
return themesList[i];
}
doc.scroll(_.throttle(function() {
updateMetrics();
var i = findCurrentTheme();
if (i >= 0 && i != currentTheme) {
switchTheme(findCurrentTheme());
}
// Undo sidebar-truncation fix in goToTheme if user goes
// into free-scrolling mode.
if (i === 0) {
$('.sidebar').removeClass('lineup');
}
}, 250));
doc.keyup(function(e) {
if (!$(queue).hasClass('shortcuts')) return;
// Ignore key-bindings when textarea focused.
if (fieldFocused(e) && e.which != z.keys.ENTER) return;
// For using Enter to submit textareas.
if (e.which == z.keys.ENTER && z.keys.ENTER in keymap) {
keymap[z.keys.ENTER]();
}
var key = String.fromCharCode(e.which).toLowerCase();
if (!(key in keymap)) {
return;
}
var action = keymap[key];
if (action && !e.ctrlKey && !e.altKey && !e.metaKey) {
themeActions[action[0]](currentTheme, action[1]);
}
});
// Pressing Enter in text field doesn't add carriage return.
$('textarea').keypress(function(e) {
if (e.keyCode == z.keys.ENTER) {
e.preventDefault();
}
});
$('.theme', queue).removeClass('active');
updateMetrics();
switchTheme(findCurrentTheme()); switchTheme(findCurrentTheme());
}
// Undo sidebar-truncation fix in goToTheme if user goes
// into free-scrolling mode.
if (i === 0) {
$('.sidebar').removeClass('lineup');
}
}, 250),
);
function updateMetrics() { doc.keyup(function (e) {
var queueHeight = $(queue).height(); if (!$(queue).hasClass('shortcuts')) return;
if (queueHeight === cacheQueueHeight) return;
cacheQueueHeight = queueHeight;
$.each(themes, function(i, obj) { // Ignore key-bindings when textarea focused.
var elem = $(obj.element); if (fieldFocused(e) && e.which != z.keys.ENTER) return;
obj.top = elem.offset().top + elem.outerHeight()/2;
});
}
function getThemeParent(elem) { // For using Enter to submit textareas.
// Given an element (like an approve button), if (e.which == z.keys.ENTER && z.keys.ENTER in keymap) {
// return the theme for which it is related to. keymap[z.keys.ENTER]();
return $(elem).closest('.theme').data('id'); }
}
function goToTheme(i, delay, duration) { var key = String.fromCharCode(e.which).toLowerCase();
delay = delay || 0;
duration = duration || 250;
setTimeout(function() {
if (i >= 0 && i < themes.length) {
$(themes[i].element).scrollTo({ duration: duration, marginTop: 20 });
}
}, delay);
$('.rq-dropdown').hide();
}
function switchTheme(i) { if (!(key in keymap)) {
if (!themes[currentTheme]) { return;
return; }
}
$(themes[currentTheme].element).removeClass('active'); var action = keymap[key];
$(themes[i].element).addClass('active'); if (action && !e.ctrlKey && !e.altKey && !e.metaKey) {
vertAlignSidebar(win, $('.theme.active')); themeActions[action[0]](currentTheme, action[1]);
currentTheme = i; }
$('.rq-dropdown').hide(); });
}
function findCurrentTheme() { // Pressing Enter in text field doesn't add carriage return.
// Uses location of the window within the page to determine $('textarea').keypress(function (e) {
// which theme we're currently looking at. if (e.keyCode == z.keys.ENTER) {
e.preventDefault();
}
});
// $(window).scroll() fires too early. $('.theme', queue).removeClass('active');
if (!themes[currentTheme]) { updateMetrics();
return 0; switchTheme(findCurrentTheme());
}
var pageTop = win.scrollTop(); function updateMetrics() {
if (pageTop <= themes[currentTheme].top) { var queueHeight = $(queue).height();
for (var i = currentTheme - 1; i >= 0; i--) { if (queueHeight === cacheQueueHeight) return;
if (themes[i].top < pageTop) { cacheQueueHeight = queueHeight;
break;
}
}
return i+1;
} else {
for (var i = currentTheme; i < themes.length; i++) {
// Scroll down the themes until we find a theme
// that is at the top of our page. That is our current
// theme.
if (pageTop <= themes[i].top) {
return i;
}
}
}
}
var keymap = { $.each(themes, function (i, obj) {
j: ['next', null], var elem = $(obj.element);
k: ['prev', null], obj.top = elem.offset().top + elem.outerHeight() / 2;
a: ['approve', null],
r: ['reject_reason', null],
d: ['duplicate', null],
f: ['flag', null],
m: ['moreinfo', null]
};
// keymap[0] = ['other_reject_reason', 0];
for (var j =1; j <= 9; j++) {
keymap[j] = ['reject', j];
}
function setReviewed(i, action) {
var ac = actionConstants;
var actionMap = {};
actionMap[ac.moreinfo] = [gettext('Requested Info'), 'blue'];
actionMap[ac.flagged] = [gettext('Flagged'), 'red'];
actionMap[ac.duplicate] = [gettext('Duplicate'), 'red'];
actionMap[ac.reject] = [gettext('Rejected'), 'red'];
actionMap[ac.approve] = [gettext('Approved'), 'green'];
var text = actionMap[action][0];
var color = actionMap[action][1];
$(nthTheme(i)).addClass('reviewed');
$('.status', nthTheme(i)).removeClass('red blue green')
.addClass('reviewed ' + color).find('.status-text').text(text);
$('#reviewed-count').text($('div.theme.reviewed').length);
if ($(queue).hasClass('advance')) {
goToTheme(i + 1, 250);
} else {
delete keymap[z.keys.ENTER];
$('.rq-dropdown').hide();
}
}
var isRejecting = false;
$(document).on('click', 'li.reject_reason', _pd(function(e) {
if (isRejecting) {
var i = getThemeParent(e.currentTarget);
var rejectId = $(this).data('id');
if (rejectId === 0) {
themeActions.other_reject_reason(i);
} else {
themeActions.reject(i, rejectId);
}
}
}));
var themeActions = {
next: function (i) { goToTheme(i + 1); },
prev: function (i) { goToTheme(i - 1); },
approve: function (i) {
$('input.action', nthTheme(i)).val(actionConstants.approve);
setReviewed(i, actionConstants.approve);
},
reject_reason: function (i) {
// Open up dropdown of rejection reasons and set up
// key and click-bindings for choosing a reason. This
// function does not actually do the rejecting as the
// rejecting is only done once a reason is supplied.
$('.rq-dropdown:not(.reject-reason-dropdown)').hide();
$('.reject-reason-dropdown', nthTheme(i)).toggle();
isRejecting = true;
},
other_reject_reason: function(i) {
if (!isRejecting) { return; }
// Open text area to enter in a custom rejection reason.
$('.rq-dropdown:not(.reject-reason-detail-dropdown)').hide();
$('.reject-reason-detail-dropdown', nthTheme(i)).toggle();
var textArea = $('.reject-reason-detail-dropdown textarea', nthTheme(i)).focus();
// Submit link/URL of the duplicate.
var submit = function() {
if (textArea.val()) {
$('input.comment', nthTheme(i)).val(textArea.val());
textArea.blur();
themeActions.reject(i, 0);
} else {
$('.reject-reason-detail-dropdown .error-required',
nthTheme(i)).show();
}
};
keymap[z.keys.ENTER] = submit;
$('.reject-reason-detail-dropdown button').click(_pd(submit));
},
reject: function(i, rejectId) {
if (!isRejecting) { return; }
// Given the rejection reason, does the actual rejection of
// the Theme.
$('input.action', nthTheme(i)).val(actionConstants.reject);
$('input.reject-reason', nthTheme(i)).val(rejectId);
setReviewed(i, actionConstants.reject);
isRejecting = false;
},
duplicate: function(i) {
// Open up dropdown to enter ID/URL of duplicate.
$('.rq-dropdown:not(.duplicate-dropdown)').hide();
$('.duplicate-dropdown', nthTheme(i)).toggle();
var textArea = $('.duplicate-dropdown textarea', nthTheme(i)).focus();
// Submit link/URL of the duplicate.
var submit = function() {
if (textArea.val()) {
$('input.action', nthTheme(i)).val(actionConstants.duplicate);
$('input.comment', nthTheme(i)).val(textArea.val());
textArea.blur();
setReviewed(i, actionConstants.duplicate);
} else {
$('.duplicate-dropdown .error-required',
nthTheme(i)).show();
}
};
keymap[z.keys.ENTER] = submit;
$('.duplicate-dropdown button').click(_pd(submit));
},
flag: function(i) {
// Open up dropdown to enter reason for flagging.
$('.rq-dropdown:not(.flag-dropdown)').hide();
$('.flag-dropdown', nthTheme(i)).toggle();
var textArea = $('.flag-dropdown textarea', nthTheme(i)).focus();
// Submit link/URL of the flag.
var submit = function() {
if (textArea.val()) {
$('input.action', nthTheme(i)).val(actionConstants.flag);
$('input.comment', nthTheme(i)).val(textArea.val());
textArea.blur();
setReviewed(i, actionConstants.flagged);
} else {
$('.flag-dropdown .error-required',
nthTheme(i)).show();
}
};
keymap[z.keys.ENTER] = submit;
$('.flag-dropdown button').click(_pd(submit));
},
moreinfo: function(i) {
// Open up dropdown to enter ID/URL of moreinfo.
$('.rq-dropdown:not(.moreinfo-dropdown)').hide();
$('.moreinfo-dropdown', nthTheme(i)).toggle();
var textArea = $('.moreinfo-dropdown textarea', nthTheme(i)).focus();
// Submit link/URL of the moreinfo.
var submit = function() {
if (textArea.val()) {
$('input.action', nthTheme(i)).val(actionConstants.moreinfo);
$('input.comment', nthTheme(i)).val(textArea.val());
textArea.blur();
setReviewed(i, actionConstants.moreinfo);
} else {
$('.moreinfo-dropdown .error-required',
nthTheme(i)).show();
}
};
keymap[z.keys.ENTER] = submit;
$('.moreinfo-dropdown button').click(_pd(submit));
},
clearReview: function(i) {
$('input.action, input.comment, input.reject-reason',
nthTheme(i)).prop('value', '');
$(nthTheme(i)).removeClass('reviewed');
$('.status', nthTheme(i)).removeClass('reviewed');
$('#reviewed-count').text($('div.theme.reviewed').length);
}
};
$(document).on('click', 'button.approve', _pd(function(e) {
themeActions.approve(getThemeParent(e.currentTarget));
}))
.on('click', 'button.reject', _pd(function(e) {
themeActions.reject_reason(getThemeParent(e.currentTarget));
}))
.on('click', 'button.duplicate', _pd(function(e) {
themeActions.duplicate(getThemeParent(e.currentTarget));
}))
.on('click', 'button.flag', _pd(function(e) {
themeActions.flag(getThemeParent(e.currentTarget));
}))
.on('click', 'button.moreinfo', _pd(function(e) {
themeActions.moreinfo(getThemeParent(e.currentTarget));
}))
.on('click', '.clear-review', _pd(function(e) {
themeActions.clearReview(getThemeParent(e.currentTarget));
}));
}); });
}; }
$.fn.themeQueueOptions = function(queueSelector) { function getThemeParent(elem) {
return this.each(function() { // Given an element (like an approve button),
var self = this; // return the theme for which it is related to.
return $(elem).closest('.theme').data('id');
}
$('input', self).click(onChange); function goToTheme(i, delay, duration) {
$('select', self).change(onChange); delay = delay || 0;
onChange(); duration = duration || 250;
setTimeout(function () {
if (i >= 0 && i < themes.length) {
$(themes[i].element).scrollTo({
duration: duration,
marginTop: 20,
});
}
}, delay);
$('.rq-dropdown').hide();
}
function onChange(e) { function switchTheme(i) {
var category = $('#rq-category', self).val(); if (!themes[currentTheme]) {
var advance = $('#rq-advance:checked', self).val(); return;
var shortcuts = $('#rq-shortcuts:checked', self).val(); }
$(queueSelector) $(themes[currentTheme].element).removeClass('active');
.toggleClass('advance', !!advance) $(themes[i].element).addClass('active');
.toggleClass('shortcuts', !!shortcuts); vertAlignSidebar(win, $('.theme.active'));
currentTheme = i;
$('.rq-dropdown').hide();
}
function findCurrentTheme() {
// Uses location of the window within the page to determine
// which theme we're currently looking at.
// $(window).scroll() fires too early.
if (!themes[currentTheme]) {
return 0;
}
var pageTop = win.scrollTop();
if (pageTop <= themes[currentTheme].top) {
for (var i = currentTheme - 1; i >= 0; i--) {
if (themes[i].top < pageTop) {
break;
} }
}); }
}; return i + 1;
} else {
for (var i = currentTheme; i < themes.length; i++) {
// Scroll down the themes until we find a theme
// that is at the top of our page. That is our current
// theme.
if (pageTop <= themes[i].top) {
return i;
}
}
}
}
var keymap = {
j: ['next', null],
k: ['prev', null],
a: ['approve', null],
r: ['reject_reason', null],
d: ['duplicate', null],
f: ['flag', null],
m: ['moreinfo', null],
};
// keymap[0] = ['other_reject_reason', 0];
for (var j = 1; j <= 9; j++) {
keymap[j] = ['reject', j];
}
function setReviewed(i, action) {
var ac = actionConstants;
var actionMap = {};
actionMap[ac.moreinfo] = [gettext('Requested Info'), 'blue'];
actionMap[ac.flagged] = [gettext('Flagged'), 'red'];
actionMap[ac.duplicate] = [gettext('Duplicate'), 'red'];
actionMap[ac.reject] = [gettext('Rejected'), 'red'];
actionMap[ac.approve] = [gettext('Approved'), 'green'];
var text = actionMap[action][0];
var color = actionMap[action][1];
$(nthTheme(i)).addClass('reviewed');
$('.status', nthTheme(i))
.removeClass('red blue green')
.addClass('reviewed ' + color)
.find('.status-text')
.text(text);
$('#reviewed-count').text($('div.theme.reviewed').length);
if ($(queue).hasClass('advance')) {
goToTheme(i + 1, 250);
} else {
delete keymap[z.keys.ENTER];
$('.rq-dropdown').hide();
}
}
var isRejecting = false;
$(document).on(
'click',
'li.reject_reason',
_pd(function (e) {
if (isRejecting) {
var i = getThemeParent(e.currentTarget);
var rejectId = $(this).data('id');
if (rejectId === 0) {
themeActions.other_reject_reason(i);
} else {
themeActions.reject(i, rejectId);
}
}
}),
);
var themeActions = {
next: function (i) {
goToTheme(i + 1);
},
prev: function (i) {
goToTheme(i - 1);
},
approve: function (i) {
$('input.action', nthTheme(i)).val(actionConstants.approve);
setReviewed(i, actionConstants.approve);
},
reject_reason: function (i) {
// Open up dropdown of rejection reasons and set up
// key and click-bindings for choosing a reason. This
// function does not actually do the rejecting as the
// rejecting is only done once a reason is supplied.
$('.rq-dropdown:not(.reject-reason-dropdown)').hide();
$('.reject-reason-dropdown', nthTheme(i)).toggle();
isRejecting = true;
},
other_reject_reason: function (i) {
if (!isRejecting) {
return;
}
// Open text area to enter in a custom rejection reason.
$('.rq-dropdown:not(.reject-reason-detail-dropdown)').hide();
$('.reject-reason-detail-dropdown', nthTheme(i)).toggle();
var textArea = $(
'.reject-reason-detail-dropdown textarea',
nthTheme(i),
).focus();
// Submit link/URL of the duplicate.
var submit = function () {
if (textArea.val()) {
$('input.comment', nthTheme(i)).val(textArea.val());
textArea.blur();
themeActions.reject(i, 0);
} else {
$(
'.reject-reason-detail-dropdown .error-required',
nthTheme(i),
).show();
}
};
keymap[z.keys.ENTER] = submit;
$('.reject-reason-detail-dropdown button').click(_pd(submit));
},
reject: function (i, rejectId) {
if (!isRejecting) {
return;
}
// Given the rejection reason, does the actual rejection of
// the Theme.
$('input.action', nthTheme(i)).val(actionConstants.reject);
$('input.reject-reason', nthTheme(i)).val(rejectId);
setReviewed(i, actionConstants.reject);
isRejecting = false;
},
duplicate: function (i) {
// Open up dropdown to enter ID/URL of duplicate.
$('.rq-dropdown:not(.duplicate-dropdown)').hide();
$('.duplicate-dropdown', nthTheme(i)).toggle();
var textArea = $('.duplicate-dropdown textarea', nthTheme(i)).focus();
// Submit link/URL of the duplicate.
var submit = function () {
if (textArea.val()) {
$('input.action', nthTheme(i)).val(actionConstants.duplicate);
$('input.comment', nthTheme(i)).val(textArea.val());
textArea.blur();
setReviewed(i, actionConstants.duplicate);
} else {
$('.duplicate-dropdown .error-required', nthTheme(i)).show();
}
};
keymap[z.keys.ENTER] = submit;
$('.duplicate-dropdown button').click(_pd(submit));
},
flag: function (i) {
// Open up dropdown to enter reason for flagging.
$('.rq-dropdown:not(.flag-dropdown)').hide();
$('.flag-dropdown', nthTheme(i)).toggle();
var textArea = $('.flag-dropdown textarea', nthTheme(i)).focus();
// Submit link/URL of the flag.
var submit = function () {
if (textArea.val()) {
$('input.action', nthTheme(i)).val(actionConstants.flag);
$('input.comment', nthTheme(i)).val(textArea.val());
textArea.blur();
setReviewed(i, actionConstants.flagged);
} else {
$('.flag-dropdown .error-required', nthTheme(i)).show();
}
};
keymap[z.keys.ENTER] = submit;
$('.flag-dropdown button').click(_pd(submit));
},
moreinfo: function (i) {
// Open up dropdown to enter ID/URL of moreinfo.
$('.rq-dropdown:not(.moreinfo-dropdown)').hide();
$('.moreinfo-dropdown', nthTheme(i)).toggle();
var textArea = $('.moreinfo-dropdown textarea', nthTheme(i)).focus();
// Submit link/URL of the moreinfo.
var submit = function () {
if (textArea.val()) {
$('input.action', nthTheme(i)).val(actionConstants.moreinfo);
$('input.comment', nthTheme(i)).val(textArea.val());
textArea.blur();
setReviewed(i, actionConstants.moreinfo);
} else {
$('.moreinfo-dropdown .error-required', nthTheme(i)).show();
}
};
keymap[z.keys.ENTER] = submit;
$('.moreinfo-dropdown button').click(_pd(submit));
},
clearReview: function (i) {
$(
'input.action, input.comment, input.reject-reason',
nthTheme(i),
).prop('value', '');
$(nthTheme(i)).removeClass('reviewed');
$('.status', nthTheme(i)).removeClass('reviewed');
$('#reviewed-count').text($('div.theme.reviewed').length);
},
};
$(document)
.on(
'click',
'button.approve',
_pd(function (e) {
themeActions.approve(getThemeParent(e.currentTarget));
}),
)
.on(
'click',
'button.reject',
_pd(function (e) {
themeActions.reject_reason(getThemeParent(e.currentTarget));
}),
)
.on(
'click',
'button.duplicate',
_pd(function (e) {
themeActions.duplicate(getThemeParent(e.currentTarget));
}),
)
.on(
'click',
'button.flag',
_pd(function (e) {
themeActions.flag(getThemeParent(e.currentTarget));
}),
)
.on(
'click',
'button.moreinfo',
_pd(function (e) {
themeActions.moreinfo(getThemeParent(e.currentTarget));
}),
)
.on(
'click',
'.clear-review',
_pd(function (e) {
themeActions.clearReview(getThemeParent(e.currentTarget));
}),
);
});
};
$.fn.themeQueueOptions = function (queueSelector) {
return this.each(function () {
var self = this;
$('input', self).click(onChange);
$('select', self).change(onChange);
onChange();
function onChange(e) {
var category = $('#rq-category', self).val();
var advance = $('#rq-advance:checked', self).val();
var shortcuts = $('#rq-shortcuts:checked', self).val();
$(queueSelector)
.toggleClass('advance', !!advance)
.toggleClass('shortcuts', !!shortcuts);
}
});
};
})(jQuery); })(jQuery);
function vertAlignSidebar(win) { function vertAlignSidebar(win) {
var activeThemeTop = ($('.theme.active').offset().top - var activeThemeTop = $('.theme.active').offset().top - win.scrollTop();
win.scrollTop()); $('.sidebar .align.fixed').css('top', activeThemeTop + 'px');
$('.sidebar .align.fixed').css('top', activeThemeTop + 'px');
} }
$(document).ready(function () {
var $theme_queue = $('.theme-queue');
if ($theme_queue.length) {
$('.zoombox').zoomBox();
$('.zoombox img').previewPersona();
$theme_queue.themeQueue();
$('.sidebar').themeQueueOptions('.theme-queue');
$('#commit').click(
_pd(function (e) {
$('#theme-queue-form').submit();
}),
);
$(document).ready(function() { // Align sidebar with active theme.
var $theme_queue = $('.theme-queue'); if ($('.theme.active').length) {
if ($theme_queue.length) { var win = $(window);
$('.zoombox').zoomBox(); win.scroll(
$('.zoombox img').previewPersona(); _.throttle(function () {
$theme_queue.themeQueue(); vertAlignSidebar(win);
$('.sidebar').themeQueueOptions('.theme-queue'); }, 100),
$('#commit').click(_pd(function(e) { );
$('#theme-queue-form').submit(); vertAlignSidebar(win);
}));
// Align sidebar with active theme.
if ($('.theme.active').length) {
var win = $(window);
win.scroll(_.throttle(function() {
vertAlignSidebar(win);
}, 100));
vertAlignSidebar(win);
}
// If daily message is present, align fixed sidebar.
if (['none', undefined].indexOf($('.daily-message').css('display')) < 0) {
var $sidebar = $('.sidebar .align.fixed');
var top = parseInt($sidebar.css('top'), 10) + 82;
$sidebar.css('top', top + 'px');
}
} }
if ($('.theme-search').length) { // If daily message is present, align fixed sidebar.
initSearch(); if (['none', undefined].indexOf($('.daily-message').css('display')) < 0) {
var $sidebar = $('.sidebar .align.fixed');
var top = parseInt($sidebar.css('top'), 10) + 82;
$sidebar.css('top', top + 'px');
} }
}
if ($('.theme-search').length) {
initSearch();
}
}); });
function initSearch() { function initSearch() {
var no_results = '<p class="no-results">' + gettext('No results found') + '</p>'; var no_results =
'<p class="no-results">' + gettext('No results found') + '</p>';
var $clear = $('.clear-queue-search'), var $clear = $('.clear-queue-search'),
$appQueue = $('.search-toggle'), $appQueue = $('.search-toggle'),
$search = $('.queue-search'), $search = $('.queue-search'),
$searchIsland = $('#search-island'); $searchIsland = $('#search-island');
if ($search.length) { if ($search.length) {
var apiUrl = $search.data('api-url'); var apiUrl = $search.data('api-url');
var review_url = $search.data('review-url'); var review_url = $search.data('review-url');
var statuses = $searchIsland.data('statuses'); var statuses = $searchIsland.data('statuses');
$('form', $search).submit(_pd(function() { $('form', $search).submit(
var $form = $(this); _pd(function () {
$.get(apiUrl, $form.serialize()).done(function(data) { var $form = $(this);
// Hide app queue. $.get(apiUrl, $form.serialize()).done(function (data) {
$appQueue.hide(); // Hide app queue.
$clear.show(); $appQueue.hide();
// Show results. $clear.show();
if (data.meta.total_count === 0) { // Show results.
$searchIsland.html(no_results).show().removeClass('hidden'); if (data.meta.total_count === 0) {
} else { $searchIsland.html(no_results).show().removeClass('hidden');
var results = []; } else {
$.each(data.objects, function(i, item) { var results = [];
item = buildThemeResultRow(item, review_url, $.each(data.objects, function (i, item) {
statuses); item = buildThemeResultRow(item, review_url, statuses);
results.push(search_result_row_template(item)); results.push(search_result_row_template(item));
});
$searchIsland.html(search_results_template({rows: results.join('')}));
$searchIsland.removeClass('hidden').show();
}
}); });
})); $searchIsland.html(
} search_results_template({ rows: results.join('') }),
);
$searchIsland.removeClass('hidden').show();
}
});
}),
);
}
} }
function buildThemeResultRow(theme, review_url, statuses) { function buildThemeResultRow(theme, review_url, statuses) {
// Add some extra pretty attrs for the template. // Add some extra pretty attrs for the template.
theme.name = theme.name[0]; theme.name = theme.name[0];
// Rather resolve URLs in backend, infer from slug. // Rather resolve URLs in backend, infer from slug.
theme.review_url = review_url.replace( theme.review_url = review_url.replace('__slug__', theme.slug);
'__slug__', theme.slug); theme.status = statuses[theme.status];
theme.status = statuses[theme.status]; return theme;
return theme;
} }

Просмотреть файл

@ -24,22 +24,27 @@ _.template(`
/* The following is the above commented template, pre-compiled. */ /* The following is the above commented template, pre-compiled. */
function search_results_template(obj){ function search_results_template(obj) {
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');}; var __t,
with(obj||{}){ __p = '',
__p+='\n <table id="search-queue" class="data-grid items">\n <thead>\n <tr class="listing-header">\n <th>'+ __j = Array.prototype.join,
((__t=( gettext('Theme') ))==null?'':_.escape(__t))+ print = function () {
'</th>\n <th>'+ __p += __j.call(arguments, '');
((__t=( gettext('Reviewer') ))==null?'':_.escape(__t))+ };
'</th>\n <th>'+ with (obj || {}) {
((__t=( gettext('Status') ))==null?'':_.escape(__t))+ __p +=
'</th>\n </tr>\n </thead>\n <tbody>'+ '\n <table id="search-queue" class="data-grid items">\n <thead>\n <tr class="listing-header">\n <th>' +
((__t=( rows ))==null?'':__t)+ ((__t = gettext('Theme')) == null ? '' : _.escape(__t)) +
'</tbody>\n </table>\n'; '</th>\n <th>' +
((__t = gettext('Reviewer')) == null ? '' : _.escape(__t)) +
'</th>\n <th>' +
((__t = gettext('Status')) == null ? '' : _.escape(__t)) +
'</th>\n </tr>\n </thead>\n <tbody>' +
((__t = rows) == null ? '' : __t) +
'</tbody>\n </table>\n';
}
return __p;
} }
return __p;
}
/* /*
_.template(` _.template(`
@ -59,24 +64,32 @@ _.template(`
*/ */
/* The following is the above commented template, pre-compiled. */ /* The following is the above commented template, pre-compiled. */
function search_result_row_template(obj){ function search_result_row_template(obj) {
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');}; var __t,
with(obj||{}){ __p = '',
__p+='\n <tr class="addon-row">\n <td class="app-name">\n <span class="addon-id">'+ __j = Array.prototype.join,
((__t=( id ))==null?'':__t)+ print = function () {
'</span>\n <a href="'+ __p += __j.call(arguments, '');
((__t=( review_url ))==null?'':__t)+ };
'">'+ with (obj || {}) {
((__t=( name ))==null?'':_.escape(__t))+ __p +=
'</a>\n </td>\n <td>\n '; '\n <tr class="addon-row">\n <td class="app-name">\n <span class="addon-id">' +
if (typeof reviewer !== "undefined") { ((__t = id) == null ? '' : __t) +
__p+='\n '+ '</span>\n <a href="' +
((__t=( reviewer ))==null?'':_.escape(__t))+ ((__t = review_url) == null ? '' : __t) +
'\n '; '">' +
} ((__t = name) == null ? '' : _.escape(__t)) +
__p+='\n </td>\n <td>'+ '</a>\n </td>\n <td>\n ';
((__t=( status ))==null?'':__t)+ if (typeof reviewer !== 'undefined') {
'</td>\n </tr>'; __p +=
} '\n ' +
return __p; ((__t = reviewer) == null ? '' : _.escape(__t)) +
'\n ';
}
__p +=
'\n </td>\n <td>' +
((__t = status) == null ? '' : __t) +
'</td>\n </tr>';
}
return __p;
} }

Просмотреть файл

@ -1,58 +1,66 @@
$.fn.truncate = function(opts) { $.fn.truncate = function (opts) {
this.each(function() { this.each(function () {
truncate(this, opts); truncate(this, opts);
}); });
return this; return this;
}; };
$.fn.untruncate = function() { $.fn.untruncate = function () {
this.each(function() { this.each(function () {
var $el = $(this), var $el = $(this),
oTxt = $el.attr("oldtext"); oTxt = $el.attr('oldtext');
if (oTxt) { if (oTxt) {
$el.text(oTxt); $el.text(oTxt);
}
});
return this;
};
$.fn.lineclamp = function(lines) {
// This function limits the number of visible `lines` of text. Overflown
// text is gracefully ellipsed: http://en.wiktionary.org/wiki/ellipse#Verb.
if (!lines) {
return this;
} }
return this.each(function() { });
var $this = $(this), return this;
lh = $this.css('line-height');
if (typeof lh == 'string' && lh.substr(-2) == 'px') {
lh = parseFloat(lh.replace('px', ''));
var maxHeight = Math.ceil(lh) * lines,
truncated;
if ((this.scrollHeight - maxHeight) > 2) {
$this.css({'height': maxHeight + 2, 'overflow': 'hidden',
'text-overflow': 'ellipsis'});
// Add an ellipsis.
$this.truncate({dir: 'v'});
} else {
$this.css({'max-height': maxHeight, 'overflow': 'hidden',
'text-overflow': 'ellipsis'});
}
}
});
}; };
$.fn.linefit = function(lines) { $.fn.lineclamp = function (lines) {
// This function shrinks text to fit on one line. // This function limits the number of visible `lines` of text. Overflown
var min_font_size = 7; // text is gracefully ellipsed: http://en.wiktionary.org/wiki/ellipse#Verb.
lines = lines || 1; if (!lines) {
return this.each(function() { return this;
var $this = $(this), }
fs = parseFloat($this.css('font-size').replace('px', '')), return this.each(function () {
max_height = Math.ceil(parseFloat($this.css('line-height').replace('px', ''))) * lines, var $this = $(this),
height = $this.height(); lh = $this.css('line-height');
while (height > max_height && fs > min_font_size) { if (typeof lh == 'string' && lh.substr(-2) == 'px') {
// Repeatedly shrink the text by 0.5px until all the text fits. lh = parseFloat(lh.replace('px', ''));
fs -= .5; var maxHeight = Math.ceil(lh) * lines,
$this.css('font-size', fs); truncated;
height = $this.height(); if (this.scrollHeight - maxHeight > 2) {
} $this.css({
}); height: maxHeight + 2,
overflow: 'hidden',
'text-overflow': 'ellipsis',
});
// Add an ellipsis.
$this.truncate({ dir: 'v' });
} else {
$this.css({
'max-height': maxHeight,
overflow: 'hidden',
'text-overflow': 'ellipsis',
});
}
}
});
};
$.fn.linefit = function (lines) {
// This function shrinks text to fit on one line.
var min_font_size = 7;
lines = lines || 1;
return this.each(function () {
var $this = $(this),
fs = parseFloat($this.css('font-size').replace('px', '')),
max_height =
Math.ceil(parseFloat($this.css('line-height').replace('px', ''))) *
lines,
height = $this.height();
while (height > max_height && fs > min_font_size) {
// Repeatedly shrink the text by 0.5px until all the text fits.
fs -= 0.5;
$this.css('font-size', fs);
height = $this.height();
}
});
}; };

Просмотреть файл

@ -1,4 +1,5 @@
// Javascript's unicode support sucks a lot. // Javascript's unicode support sucks a lot.
// This allows us to match Unicode letters. // This allows us to match Unicode letters.
z.unicode_letters = "\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0525\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0621-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971\u0972\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3D\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC\u0EDD\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8B\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u2094\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCB\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA65F\uA662-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B\uA78C\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA2D\uFA30-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC"; z.unicode_letters =
$(document).trigger('unicode_loaded'); '\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0525\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0621-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971\u0972\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3D\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC\u0EDD\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8B\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u2094\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCB\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA65F\uA662-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B\uA78C\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA2D\uFA30-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC';
$(document).trigger('unicode_loaded');

Просмотреть файл

@ -1,25 +1,27 @@
// Hijack "Admin / Editor Log in" context menuitem. // Hijack "Admin / Editor Log in" context menuitem.
$('#admin-login').click(function() { $('#admin-login').click(function () {
window.location = $(this).attr('data-url'); window.location = $(this).attr('data-url');
}); });
// Recaptcha // Recaptcha
var RecaptchaOptions = { theme : 'custom' }; var RecaptchaOptions = { theme: 'custom' };
$('#recaptcha_different').click(function(e) { $('#recaptcha_different').click(function (e) {
e.preventDefault(); e.preventDefault();
Recaptcha.reload(); Recaptcha.reload();
}); });
$('#recaptcha_audio').click(function(e) { $('#recaptcha_audio').click(function (e) {
e.preventDefault(); e.preventDefault();
var toggleType = this.getAttribute('data-nextType') || 'audio'; var toggleType = this.getAttribute('data-nextType') || 'audio';
Recaptcha.switch_type(toggleType); Recaptcha.switch_type(toggleType);
this.setAttribute('data-nextType', toggleType === 'audio' ? 'image' : 'audio'); this.setAttribute(
'data-nextType',
toggleType === 'audio' ? 'image' : 'audio',
);
}); });
$('#recaptcha_help').click(function(e) { $('#recaptcha_help').click(function (e) {
e.preventDefault(); e.preventDefault();
Recaptcha.showhelp(); Recaptcha.showhelp();
}); });

Просмотреть файл

@ -1,477 +1,528 @@
$(document).ready(function() { $(document).ready(function () {
if ($('.addon-validator-suite').length) {
if ($('.addon-validator-suite').length) { initValidator();
initValidator(); }
}
}); });
function initValidator($doc) { function initValidator($doc) {
$doc = $doc || $(document); $doc = $doc || $(document);
function inherit(OtherClass, constructor) { function inherit(OtherClass, constructor) {
var NewClass = function() { var NewClass = function () {
OtherClass.apply(this, arguments); OtherClass.apply(this, arguments);
if (typeof constructor !== 'undefined') { if (typeof constructor !== 'undefined') {
constructor.apply(this, arguments); constructor.apply(this, arguments);
} }
} };
$.extend(NewClass.prototype, OtherClass.prototype); $.extend(NewClass.prototype, OtherClass.prototype);
return NewClass; return NewClass;
}
function emptyFn() {
return null;
}
function ResultsTier($suite, tierId, options) {
if (typeof options === 'undefined') {
options = {};
}
if (typeof options.app === 'undefined') {
options.app = null;
}
if (typeof options.testsWereRun === 'undefined') {
options.testsWereRun = true;
}
this.$results = $('.results', $suite);
this.app = options.app;
this.testsWereRun = options.testsWereRun;
this.counts = { error: 0, warning: 0, notice: 0 };
this.tierId = tierId;
this.$suite = $suite;
this.$dom = $('#suite-results-tier-' + tierId, $suite);
if (!this.$dom.length) {
this.$dom = this.createDom();
this.$results.append(this.$dom);
}
this.$tierResults = $('.tier-results', this.$dom);
this.wakeUp();
}
ResultsTier.prototype.clear = function () {
this.$tierResults.empty();
};
ResultsTier.prototype.tallyMsgType = function (type_) {
this.counts[type_] += 1;
};
ResultsTier.prototype.createDom = function () {
var $tier = $($('.template', this.$suite).html().trim());
$tier.attr('id', 'suite-results-tier-' + this.tierId);
return $tier;
};
ResultsTier.prototype.summarize = function () {
var sm = resultSummary(
this.counts.error,
this.counts.warning,
this.counts.notice,
this.testsWereRun,
),
resultClass,
summaryMsg;
$('.result-summary', this.$dom)
.css('visibility', 'visible')
.empty()
.text(sm);
if (this.counts.error) {
resultClass = 'tests-failed';
} else if (this.counts.warning) {
resultClass = 'tests-passed-warnings';
} else if (this.counts.notice) {
resultClass = 'tests-passed-notices';
} else {
if (this.testsWereRun) {
summaryMsg = gettext('All tests passed successfully.');
resultClass = 'tests-passed';
} else {
summaryMsg = gettext('These tests were not run.');
resultClass = 'tests-notrun';
// No summary since no tests were run:
$('.result-summary', this.$dom).html('&nbsp;');
}
this.$tierResults.append('<span>' + summaryMsg + '</span>');
}
this.$tierResults
.removeClass(
'ajax-loading tests-failed ' +
'tests-passed tests-passed-warnings ' +
'tests-passed-notices tests-notrun',
)
.addClass(resultClass);
if ($('.test-tier', this.$suite).length) {
this.topSummary();
}
return this.counts;
};
ResultsTier.prototype.topSummary = function () {
var $top = $(
'[class~="test-tier"]' + '[data-tier="' + this.tierId + '"]',
this.$suite,
),
summaryMsg = resultSummary(
this.counts.error,
this.counts.warning,
this.counts.notice,
this.testsWereRun,
);
$('.tier-summary', $top).text(summaryMsg);
$top.removeClass(
'ajax-loading',
'tests-failed',
'tests-passed',
'tests-notrun',
);
if (this.counts.error > 0) {
$top.addClass('tests-failed');
} else if (this.counts.warning > 0) {
$top.addClass('tests-warnings');
} else if (this.testsWereRun) {
$top.addClass('tests-passed');
} else {
$top.addClass('tests-notrun');
}
};
ResultsTier.prototype.wakeUp = function () {
var $title = $('h4', this.$dom),
changeLink;
$('.tier-results', this.$dom).empty();
this.$dom.removeClass('hidden');
this.$dom.show();
if (this.app) {
// Override the title with a special app/version title
$title.text(
format(
'{0} {1} {2}',
this.app.trans[this.app.guid],
this.app.version,
gettext('Tests'),
),
);
changeLink = this.app.versionChangeLinks[
this.app.guid + ' ' + this.app.version
];
if (changeLink) {
this.$dom.prepend(
format(
'<a class="version-change-link" href="{0}">{1}</a>',
changeLink,
// L10n: Example: Changes in Firefox 5
gettext(
format(
'Changes in {0} {1}',
this.app.trans[this.app.guid],
/\d+/.exec(this.app.version),
),
),
),
);
}
} else if (!$title.text()) {
$title.text(gettext('Tests'));
}
$('.tier-results', this.$dom).removeClass('ajax-loading');
};
function MsgVisitor(suite, data) {
this.$suite = suite;
this.data = data;
this.$results = $('.results', suite);
this.msgSet = {};
this.tiers = {};
this.appTrans = null;
this.versionChangeLinks = null;
this.allCounts = { error: 0, warning: 0 };
this.fileURL = suite.data('fileUrl');
this.fileID = suite.data('fileId');
}
MsgVisitor.prototype.createTier = function (tierId, options) {
var tier = new ResultsTier(this.$suite, tierId, this.tierOptions(options));
return tier;
};
MsgVisitor.prototype.finish = function () {
var self = this;
$('.result', this.$suite).each(function (i, res) {
if (!$('.msg', res).length) {
// No messages so no tier was created.
self.getTier($('.tier-results', res).attr('data-tier'));
}
});
$.each(this.tiers, function (tierId, tier) {
var tierSum = tier.summarize();
self.allCounts.error += tierSum.error;
self.allCounts.warning += tierSum.warning;
});
};
MsgVisitor.prototype.clear = function () {
$.each(this.tiers, function (tierId, tier) {
tier.clear();
});
};
MsgVisitor.prototype.getMsgType = function (msg) {
return msg['type'];
};
MsgVisitor.prototype.getTier = function (tierId, options) {
if (typeof options === 'undefined') {
options = { app: null };
}
if (
!options.app &&
this.data.validation.ending_tier &&
this.data.validation.ending_tier < tierId
) {
options.testsWereRun = false;
}
if (typeof this.tiers[tierId] === 'undefined') {
this.tiers[tierId] = this.createTier(tierId, options);
}
return this.tiers[tierId];
};
MsgVisitor.prototype.filterMessage = function (msg) {
return !(this.hideIgnored && msg.ignored);
};
MsgVisitor.prototype.message = function (msg, options) {
if (!this.filterMessage(msg)) {
return;
} }
function emptyFn() { if (typeof this.msgSet[msg.uid] !== 'undefined') {
return null; return;
} }
this.msgSet[msg.uid] = true;
function ResultsTier($suite, tierId, options) { var tier = this.getTier(msg.tier, options),
if (typeof options === 'undefined') { msgDiv = $('<div class="msg"><h5></h5></div>'),
options = {} effectiveType = this.getMsgType(msg),
} prefix = effectiveType == 'error' ? gettext('Error') : gettext('Warning');
if (typeof options.app === 'undefined') {
options.app = null;
}
if (typeof options.testsWereRun === 'undefined') {
options.testsWereRun = true;
}
this.$results = $('.results', $suite);
this.app = options.app;
this.testsWereRun = options.testsWereRun;
this.counts = {error: 0, warning: 0, notice: 0};
this.tierId = tierId;
this.$suite = $suite;
this.$dom = $('#suite-results-tier-' + tierId, $suite);
if (!this.$dom.length) {
this.$dom = this.createDom();
this.$results.append(this.$dom);
}
this.$tierResults = $('.tier-results', this.$dom);
this.wakeUp();
}
ResultsTier.prototype.clear = function() { tier.tallyMsgType(effectiveType);
this.$tierResults.empty(); msgDiv.attr('id', 'v-msg-' + msg.uid);
}; msgDiv.addClass('msg-' + effectiveType);
ResultsTier.prototype.tallyMsgType = function(type_) { // The "message" and "description" properties are escaped and linkified
this.counts[type_] += 1; // before we receive them.
}; $('h5', msgDiv).html(msg.message); // Sanitized HTML value.
ResultsTier.prototype.createDom = function() { // The validator returns the "description" as either string, or
var $tier = $($('.template', this.$suite).html().trim()); // arrays of strings. We turn it into arrays when sanitizing.
$tier.attr('id', 'suite-results-tier-' + this.tierId); $.each(msg.description, function (i, val) {
return $tier; var $desc = $('<p>').html(val); // Sanitized HTML value.
} if (i === 0) {
$desc.prepend(format('<strong>{0}:</strong> ', prefix));
ResultsTier.prototype.summarize = function() { }
var sm = resultSummary(this.counts.error, this.counts.warning, this.counts.notice, msgDiv.append($desc);
this.testsWereRun),
resultClass, summaryMsg;
$('.result-summary', this.$dom).css('visibility', 'visible')
.empty().text(sm);
if (this.counts.error) {
resultClass = 'tests-failed';
} else if (this.counts.warning) {
resultClass = 'tests-passed-warnings';
} else if (this.counts.notice) {
resultClass = 'tests-passed-notices';
} else {
if (this.testsWereRun) {
summaryMsg = gettext('All tests passed successfully.');
resultClass = 'tests-passed';
} else {
summaryMsg = gettext('These tests were not run.');
resultClass = 'tests-notrun';
// No summary since no tests were run:
$('.result-summary', this.$dom).html('&nbsp;');
}
this.$tierResults.append('<span>' + summaryMsg + '</span>');
}
this.$tierResults.removeClass('ajax-loading tests-failed ' +
'tests-passed tests-passed-warnings ' +
'tests-passed-notices tests-notrun')
.addClass(resultClass);
if ($('.test-tier', this.$suite).length) {
this.topSummary();
}
return this.counts;
};
ResultsTier.prototype.topSummary = function() {
var $top = $('[class~="test-tier"]' +
'[data-tier="' + this.tierId + '"]', this.$suite),
summaryMsg = resultSummary(this.counts.error, this.counts.warning, this.counts.notice,
this.testsWereRun);
$('.tier-summary', $top).text(summaryMsg);
$top.removeClass('ajax-loading', 'tests-failed', 'tests-passed',
'tests-notrun');
if (this.counts.error > 0) {
$top.addClass('tests-failed');
} else if (this.counts.warning > 0) {
$top.addClass('tests-warnings');
} else if (this.testsWereRun) {
$top.addClass('tests-passed');
} else {
$top.addClass('tests-notrun');
}
};
ResultsTier.prototype.wakeUp = function() {
var $title = $('h4', this.$dom),
changeLink;
$('.tier-results', this.$dom).empty();
this.$dom.removeClass('hidden');
this.$dom.show();
if (this.app) {
// Override the title with a special app/version title
$title.text(format('{0} {1} {2}',
this.app.trans[this.app.guid],
this.app.version,
gettext('Tests')));
changeLink = this.app.versionChangeLinks[this.app.guid + ' ' +
this.app.version];
if (changeLink) {
this.$dom.prepend(
format('<a class="version-change-link" href="{0}">{1}</a>',
changeLink,
// L10n: Example: Changes in Firefox 5
gettext(format('Changes in {0} {1}',
this.app.trans[this.app.guid],
/\d+/.exec(this.app.version)))));
}
} else if (!$title.text()) {
$title.text(gettext('Tests'));
}
$('.tier-results', this.$dom).removeClass('ajax-loading');
};
function MsgVisitor(suite, data) {
this.$suite = suite;
this.data = data;
this.$results = $('.results', suite);
this.msgSet = {};
this.tiers = {};
this.appTrans = null;
this.versionChangeLinks = null;
this.allCounts = {error: 0, warning: 0};
this.fileURL = suite.data('fileUrl');
this.fileID = suite.data('fileId');
}
MsgVisitor.prototype.createTier = function(tierId, options) {
var tier = new ResultsTier(this.$suite, tierId,
this.tierOptions(options));
return tier;
};
MsgVisitor.prototype.finish = function() {
var self = this;
$('.result', this.$suite).each(function(i, res) {
if (!$('.msg', res).length) {
// No messages so no tier was created.
self.getTier($('.tier-results', res).attr('data-tier'));
}
});
$.each(this.tiers, function(tierId, tier) {
var tierSum = tier.summarize();
self.allCounts.error += tierSum.error;
self.allCounts.warning += tierSum.warning;
});
};
MsgVisitor.prototype.clear = function() {
$.each(this.tiers, function(tierId, tier) {
tier.clear();
});
};
MsgVisitor.prototype.getMsgType = function(msg) {
return msg['type'];
};
MsgVisitor.prototype.getTier = function(tierId, options) {
if (typeof options === 'undefined') {
options = {app: null};
}
if (!options.app
&& this.data.validation.ending_tier
&& this.data.validation.ending_tier < tierId) {
options.testsWereRun = false;
}
if (typeof this.tiers[tierId] === 'undefined') {
this.tiers[tierId] = this.createTier(tierId, options);
}
return this.tiers[tierId];
};
MsgVisitor.prototype.filterMessage = function(msg) {
return !(this.hideIgnored && msg.ignored)
};
MsgVisitor.prototype.message = function(msg, options) {
if (!this.filterMessage(msg)) {
return;
}
if (typeof this.msgSet[msg.uid] !== 'undefined') {
return;
}
this.msgSet[msg.uid] = true;
var tier = this.getTier(msg.tier, options),
msgDiv = $('<div class="msg"><h5></h5></div>'),
effectiveType = this.getMsgType(msg),
prefix = effectiveType == 'error' ? gettext('Error')
: gettext('Warning');
tier.tallyMsgType(effectiveType);
msgDiv.attr('id', 'v-msg-' + msg.uid);
msgDiv.addClass('msg-' + effectiveType);
// The "message" and "description" properties are escaped and linkified
// before we receive them.
$('h5', msgDiv).html(msg.message); // Sanitized HTML value.
// The validator returns the "description" as either string, or
// arrays of strings. We turn it into arrays when sanitizing.
$.each(msg.description, function(i, val) {
var $desc = $('<p>').html(val); // Sanitized HTML value.
if (i === 0) {
$desc.prepend(format('<strong>{0}:</strong> ', prefix));
}
msgDiv.append($desc);
});
if (msg.file) {
var file = msg.file;
if (typeof file !== 'string') {
// For sub-packages, this will be a list of archive paths and
// a final file path, which we need to turn into a string.
// ['foo.xpi', 'chrome/thing.jar', 'content/file.js']
file = file.join('/');
}
if (this.fileURL) {
var url = this.fileURL + '?path=' + file;
if (msg.line) {
url += "#L" + msg.line;
}
var $link = $('<a>', { href: url, text: file,
target: 'file-viewer-' + this.fileID });
} else {
// There's no file browse URL for bare file uploads, so
// just display a path without a link to the sources.
$link = $('<span>', { text: file });
}
var $context = $('<div class="context">').append(
$('<div class="file">').append($link))
if (msg.context) {
var $code = $('<div class="code"></div>');
var $lines = $('<div class="lines"></div>');
var $innerCode = $('<div class="inner-code"></div>');
$code.append($lines, $innerCode);
// The line number in the message refers to the middle
// line of the context, so adjust accordingly.
var offset = Math.floor(msg.context.length / 2);
msg.context = formatCodeIndentation(msg.context);
$.each(msg.context, function(idx, code) {
if (code != null) {
$lines.append($('<div>', { text: msg.line + idx - offset }))
$innerCode.append($('<div>', { text: code }))
}
});
$context.append($code);
} else if (msg.line && typeof msg.column !== 'undefined') {
// Normally, the line number would be displayed with the
// context. If we have no context, display it with the
// filename.
$link.text(format(gettext('{0} line {1} column {2}'), [file, msg.line, msg.column]));
} else if (msg.line) {
$link.text(format(gettext('{0} line {1}'), [file, msg.line]));
}
msgDiv.append($context);
}
$('.tier-results', tier.$dom).append(msgDiv);
};
MsgVisitor.prototype.tierOptions = function(options) {
if (options && options.app) {
options.app.trans = this.appTrans;
options.app.versionChangeLinks = this.versionChangeLinks;
}
return options;
};
function buildResults(suite, data) {
var vis,
validation = data.validation,
summaryTxt;
function sortByType(messages) {
var ordering = [
'error', 'warning', 'notice', undefined /* no type */];
return _.sortBy(messages, function(msg) {
return ordering.indexOf(msg.type);
});
}
function rebuildResults() {
vis = new MsgVisitor(suite, data);
$.each(sortByType(validation.messages), function(i, msg) {
vis.message(msg);
});
vis.finish();
if (validation.errors > 0) {
summaryTxt = gettext('Add-on failed validation.');
} else {
summaryTxt = gettext('Add-on passed validation.');
}
$('.suite-summary span', suite).text(summaryTxt);
$('.suite-summary', suite).show();
}
rebuildResults();
}
function resultSummary(numErrors, numWarnings, numNotices, testsWereRun) {
if (!testsWereRun) {
return gettext('These tests were not run.');
}
// e.g. '1 error, 3 warnings'
var errors = format(ngettext('{0} error', '{0} errors', numErrors),
[numErrors]),
warnings = format(ngettext('{0} warning', '{0} warnings', numWarnings),
[numWarnings]),
notices = format(ngettext('{0} notice', '{0} notices', numNotices),
[numNotices]);
return format('{0}, {1}, {2}', errors, warnings, notices);
}
function formatCodeIndentation(lines) {
// Replaces leading tabs with spaces, and then trims the
// smallest common indentation space from each line.
function retab(line, tabstops) {
// Replaces tabs with spaces, to match the given tab stops.
var SPACES = " ";
tabstops = Math.min(tabstops || 4, SPACES.length);
function replace_tab(full_match, non_tab) {
if (non_tab) {
position += non_tab.length;
return non_tab;
}
else {
var pos = position;
position += position % tabstops || tabstops;
return SPACES.substr(0, position - pos);
}
}
var position = 0;
return line.replace(/([^\t]+)|\t/g, replace_tab);
}
// Retab all lines and find the common indent.
var indent = Infinity;
lines = lines.map(function(line) {
// When the context line is at the start or end of the file,
// the line before or after the context line will be null.
if (line == null) {
return null;
}
// We need the replace function to run even if there's no
// whitespace, so `indent` is properly updated. Stick with
// \s* rather than \s+.
return line.replace(/^(\s*)/, function(match) {
match = retab(match);
indent = Math.min(indent, match.length);
return match;
});
});
// Trim off the common white space.
return lines.map(function(line) {
// Line may be null. Do not try to slice null.
return line && line.slice(indent);
});
}
$('.addon-validator-suite', $doc).on('validate', function(e) {
var el = $(this),
data = el.data();
if (data.annotateUrl) {
el.on('change', '.ignore-duplicates-checkbox',
function(event) {
var $target = $(event.target);
$.ajax({type: 'POST',
url: data.annotateUrl,
data: { message: $target.attr('name'),
ignore_duplicates: $target.prop('checked') || undefined },
dataType: 'json'})
});
}
if (data.validation) {
buildResults(el, {validation: data.validation})
return;
}
$('.test-tier,.tier-results', el).addClass('ajax-loading');
$.ajax({type: 'POST',
url: data.validateurl,
data: {},
success: function(data) {
if (data.validation == '') {
// Note: traceback is in data.error
data.validation = {
ending_tier: 1,
messages: [{
'type':'error',
message: gettext('Error'),
description: [
gettext('Validation task could not ' +
'complete or completed with ' +
'errors')],
tier: 1,
uid: '__global_error__'
}]
};
}
buildResults(el, data);
el.trigger('success.validation');
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
buildResults(el, {
validation: {
ending_tier: 1,
messages: [{
'type':'error',
message: gettext('Error'),
description: [gettext('Internal server error')],
tier: 1,
uid: '__global_error__'
}]
}
});
el.trigger('badresponse.validation');
},
dataType: 'json'
});
}); });
// Validate when the page loads. if (msg.file) {
$('#addon-validator-suite').trigger('validate'); var file = msg.file;
if (typeof file !== 'string') {
// For sub-packages, this will be a list of archive paths and
// a final file path, which we need to turn into a string.
// ['foo.xpi', 'chrome/thing.jar', 'content/file.js']
file = file.join('/');
}
}; if (this.fileURL) {
var url = this.fileURL + '?path=' + file;
if (msg.line) {
url += '#L' + msg.line;
}
var $link = $('<a>', {
href: url,
text: file,
target: 'file-viewer-' + this.fileID,
});
} else {
// There's no file browse URL for bare file uploads, so
// just display a path without a link to the sources.
$link = $('<span>', { text: file });
}
var $context = $('<div class="context">').append(
$('<div class="file">').append($link),
);
if (msg.context) {
var $code = $('<div class="code"></div>');
var $lines = $('<div class="lines"></div>');
var $innerCode = $('<div class="inner-code"></div>');
$code.append($lines, $innerCode);
// The line number in the message refers to the middle
// line of the context, so adjust accordingly.
var offset = Math.floor(msg.context.length / 2);
msg.context = formatCodeIndentation(msg.context);
$.each(msg.context, function (idx, code) {
if (code != null) {
$lines.append($('<div>', { text: msg.line + idx - offset }));
$innerCode.append($('<div>', { text: code }));
}
});
$context.append($code);
} else if (msg.line && typeof msg.column !== 'undefined') {
// Normally, the line number would be displayed with the
// context. If we have no context, display it with the
// filename.
$link.text(
format(gettext('{0} line {1} column {2}'), [
file,
msg.line,
msg.column,
]),
);
} else if (msg.line) {
$link.text(format(gettext('{0} line {1}'), [file, msg.line]));
}
msgDiv.append($context);
}
$('.tier-results', tier.$dom).append(msgDiv);
};
MsgVisitor.prototype.tierOptions = function (options) {
if (options && options.app) {
options.app.trans = this.appTrans;
options.app.versionChangeLinks = this.versionChangeLinks;
}
return options;
};
function buildResults(suite, data) {
var vis,
validation = data.validation,
summaryTxt;
function sortByType(messages) {
var ordering = ['error', 'warning', 'notice', undefined /* no type */];
return _.sortBy(messages, function (msg) {
return ordering.indexOf(msg.type);
});
}
function rebuildResults() {
vis = new MsgVisitor(suite, data);
$.each(sortByType(validation.messages), function (i, msg) {
vis.message(msg);
});
vis.finish();
if (validation.errors > 0) {
summaryTxt = gettext('Add-on failed validation.');
} else {
summaryTxt = gettext('Add-on passed validation.');
}
$('.suite-summary span', suite).text(summaryTxt);
$('.suite-summary', suite).show();
}
rebuildResults();
}
function resultSummary(numErrors, numWarnings, numNotices, testsWereRun) {
if (!testsWereRun) {
return gettext('These tests were not run.');
}
// e.g. '1 error, 3 warnings'
var errors = format(ngettext('{0} error', '{0} errors', numErrors), [
numErrors,
]),
warnings = format(ngettext('{0} warning', '{0} warnings', numWarnings), [
numWarnings,
]),
notices = format(ngettext('{0} notice', '{0} notices', numNotices), [
numNotices,
]);
return format('{0}, {1}, {2}', errors, warnings, notices);
}
function formatCodeIndentation(lines) {
// Replaces leading tabs with spaces, and then trims the
// smallest common indentation space from each line.
function retab(line, tabstops) {
// Replaces tabs with spaces, to match the given tab stops.
var SPACES = ' ';
tabstops = Math.min(tabstops || 4, SPACES.length);
function replace_tab(full_match, non_tab) {
if (non_tab) {
position += non_tab.length;
return non_tab;
} else {
var pos = position;
position += position % tabstops || tabstops;
return SPACES.substr(0, position - pos);
}
}
var position = 0;
return line.replace(/([^\t]+)|\t/g, replace_tab);
}
// Retab all lines and find the common indent.
var indent = Infinity;
lines = lines.map(function (line) {
// When the context line is at the start or end of the file,
// the line before or after the context line will be null.
if (line == null) {
return null;
}
// We need the replace function to run even if there's no
// whitespace, so `indent` is properly updated. Stick with
// \s* rather than \s+.
return line.replace(/^(\s*)/, function (match) {
match = retab(match);
indent = Math.min(indent, match.length);
return match;
});
});
// Trim off the common white space.
return lines.map(function (line) {
// Line may be null. Do not try to slice null.
return line && line.slice(indent);
});
}
$('.addon-validator-suite', $doc).on('validate', function (e) {
var el = $(this),
data = el.data();
if (data.annotateUrl) {
el.on('change', '.ignore-duplicates-checkbox', function (event) {
var $target = $(event.target);
$.ajax({
type: 'POST',
url: data.annotateUrl,
data: {
message: $target.attr('name'),
ignore_duplicates: $target.prop('checked') || undefined,
},
dataType: 'json',
});
});
}
if (data.validation) {
buildResults(el, { validation: data.validation });
return;
}
$('.test-tier,.tier-results', el).addClass('ajax-loading');
$.ajax({
type: 'POST',
url: data.validateurl,
data: {},
success: function (data) {
if (data.validation == '') {
// Note: traceback is in data.error
data.validation = {
ending_tier: 1,
messages: [
{
type: 'error',
message: gettext('Error'),
description: [
gettext(
'Validation task could not ' +
'complete or completed with ' +
'errors',
),
],
tier: 1,
uid: '__global_error__',
},
],
};
}
buildResults(el, data);
el.trigger('success.validation');
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
buildResults(el, {
validation: {
ending_tier: 1,
messages: [
{
type: 'error',
message: gettext('Error'),
description: [gettext('Internal server error')],
tier: 1,
uid: '__global_error__',
},
],
},
});
el.trigger('badresponse.validation');
},
dataType: 'json',
});
});
// Validate when the page loads.
$('#addon-validator-suite').trigger('validate');
}

Просмотреть файл

@ -72,10 +72,10 @@ describe(__filename, () => {
stats_stats(global.$); stats_stats(global.$);
expect($('#export_data_csv').attr('href')).toEqual( expect($('#export_data_csv').attr('href')).toEqual(
`${defaultBaseUrl}${report}-day-20191007-20191013.csv` `${defaultBaseUrl}${report}-day-20191007-20191013.csv`,
); );
expect($('#export_data_json').attr('href')).toEqual( expect($('#export_data_json').attr('href')).toEqual(
`${defaultBaseUrl}${report}-day-20191007-20191013.json` `${defaultBaseUrl}${report}-day-20191007-20191013.json`,
); );
}); });
@ -86,10 +86,10 @@ describe(__filename, () => {
stats_stats(global.$); stats_stats(global.$);
expect($('#export_data_csv').attr('href')).toEqual( expect($('#export_data_csv').attr('href')).toEqual(
`${defaultBaseUrl}${report}-day-20190914-20191013.csv` `${defaultBaseUrl}${report}-day-20190914-20191013.csv`,
); );
expect($('#export_data_json').attr('href')).toEqual( expect($('#export_data_json').attr('href')).toEqual(
`${defaultBaseUrl}${report}-day-20190914-20191013.json` `${defaultBaseUrl}${report}-day-20190914-20191013.json`,
); );
}); });
@ -116,10 +116,10 @@ describe(__filename, () => {
stats_stats(global.$, fakeSessionStorage); stats_stats(global.$, fakeSessionStorage);
expect($('#export_data_csv').attr('href')).toEqual( expect($('#export_data_csv').attr('href')).toEqual(
`${defaultBaseUrl}${report}-day-20191115-20191125.csv` `${defaultBaseUrl}${report}-day-20191115-20191125.csv`,
); );
expect($('#export_data_json').attr('href')).toEqual( expect($('#export_data_json').attr('href')).toEqual(
`${defaultBaseUrl}${report}-day-20191115-20191125.json` `${defaultBaseUrl}${report}-day-20191115-20191125.json`,
); );
}); });
}); });

Просмотреть файл

@ -74,7 +74,7 @@ commands =
[testenv:codestyle] [testenv:codestyle]
recreate = True recreate = True
commands = commands =
make setup-codestyle make setup-codestyle install_node_dependencies
make lint-codestyle make lint-codestyle
[testenv:docs] [testenv:docs]