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
lint: ## lint the code
flake8 src/ services/ tests/
$(shell npm $(NPM_ARGS) bin)/prettier --check '**'
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).
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
help_submake:
@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"
},
"dependencies": {
"@claviska/jquery-minicolors": "2.3.5",
"addons-linter": "2.1.0",
"clean-css": "4.2.3",
"clean-css-cli": "4.3.0",
"jqmodal": "1.4.2",
"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-ui": "1.12.1",
"jquery.browser": "0.1.0",
"jquery.cookie": "1.4.1",
"jszip": "3.5.0",
"less": "3.12.2",
"source-map": "0.7.3",
@ -25,6 +25,7 @@
},
"devDependencies": {
"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 ($) {
if (!$('body.change-form').length) {
// This is only for change forms.
return;
}
if (!$('body.change-form').length) {
// This is only for change forms.
return;
}
// 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
// user know which one is for which locale.
$('div.trans :input:visible').before(function() {
let $elm = $(this);
let $label = $('<label>');
$label.prop('for', $elm.attr('id'))
$label.text('[' + $elm.attr('lang') + ']');
return $label;
})
// 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
// user know which one is for which locale.
$('div.trans :input:visible').before(function () {
let $elm = $(this);
let $label = $('<label>');
$label.prop('for', $elm.attr('id'));
$label.text('[' + $elm.attr('lang') + ']');
return $label;
});
});

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

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

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

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

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

@ -1,288 +1,311 @@
(function($) {
window.Slideshow = function() {
this.itemTotal = 0;
this.currentItem = 1;
this.itemWidth = 0;
(function ($) {
window.Slideshow = function () {
this.itemTotal = 0;
this.currentItem = 1;
this.itemWidth = 0;
// Set these properties when you instantiate an instance of this object.
this.speed = 300; // the speed in milliseconds of the animation
// Set these properties when you instantiate an instance of this object.
this.speed = 300; // the speed in milliseconds of the animation
this.itemContainer = ''; // the selector for the element containing the items.
this.wrapperElement = ''; // the tagName 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.leftController = ''; // the selector for the left controller.
this.rightContorller = ''; // the selector for the right controller.
this.activeClass = ''; // the classname to indicate that a controller is active.
this.container = ''; //the complete container for all of the slideshow
this.interval = null;
this.scroll = true;
};
Slideshow.prototype.init = function() {
this.itemTotal = parseInt($(this.itemContainer+'>li').length,10);
if (this.itemTotal <= 1) {
return;
}
this.itemContainer = ''; // the selector for the element containing the items.
this.wrapperElement = ''; // the tagName 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.leftController = ''; // the selector for the left controller.
this.rightContorller = ''; // the selector for the right controller.
this.activeClass = ''; // the classname to indicate that a controller is active.
this.container = ''; //the complete container for all of the slideshow
this.interval = null;
this.scroll = true;
};
Slideshow.prototype.init = function () {
this.itemTotal = parseInt($(this.itemContainer + '>li').length, 10);
if (this.itemTotal <= 1) {
return;
}
$(this.itemContainer).wrap('<'+this.wrapperElement+' class="'+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.itemContainer).wrap(
'<' +
this.wrapperElement +
' class="' +
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;
$(self.leftController).live('click', function() {
if ($(this).hasClass(self.activeClass)) {
self.moveToItem(self.currentItem-1);
}
self.scroll = false;
return false;
});
var self = this;
$(self.leftController).live('click', function () {
if ($(this).hasClass(self.activeClass)) {
self.moveToItem(self.currentItem - 1);
}
self.scroll = false;
return false;
});
$(self.rightController).live('click', function() {
if ($(this).hasClass(self.activeClass)) {
self.moveToItem(self.currentItem+1);
}
self.scroll = false;
return false;
});
$(self.rightController).live('click', function () {
if ($(this).hasClass(self.activeClass)) {
self.moveToItem(self.currentItem + 1);
}
self.scroll = false;
return false;
});
$(self.container).mouseenter(function() {
clearInterval(self.interval);
});
$(self.container).mouseenter(function () {
clearInterval(self.interval);
});
$(self.container).on('newPopup', function() {
clearInterval(self.interval);
});
$(self.container).on('newPopup', function () {
clearInterval(self.interval);
});
$(self.container).mouseleave(function() {
self.autoRotate();
});
$(self.container).mouseleave(function () {
self.autoRotate();
});
self.autoRotate();
self.autoRotate();
$(window).resize(function() {
self.itemWidth = self.getItemWidth();
$(self.itemContainer+'>li').width(self.itemWidth+'px');
self.popToItem(self.currentItem);
});
};
$(window).resize(function () {
self.itemWidth = self.getItemWidth();
$(self.itemContainer + '>li').width(self.itemWidth + 'px');
self.popToItem(self.currentItem);
});
};
Slideshow.prototype.autoRotate = function() {
if(this.scroll) {
var that = this; //closure due to setInterval's 'this' refers to window, not the current 'this'
clearInterval(this.interval);
this.interval = setInterval(function() {
if(that.currentItem != that.itemTotal) {
that.moveToItem(that.currentItem+1);
} else {
that.moveToItem(1);
}
}, 8000);
}
};
Slideshow.prototype.autoRotate = function () {
if (this.scroll) {
var that = this; //closure due to setInterval's 'this' refers to window, not the current 'this'
clearInterval(this.interval);
this.interval = setInterval(function () {
if (that.currentItem != that.itemTotal) {
that.moveToItem(that.currentItem + 1);
} else {
that.moveToItem(1);
}
}, 8000);
}
};
Slideshow.prototype.getItemWidth = function() {
return $(this.itemContainer).parents('.'+this.wrapperClass).width();
};
Slideshow.prototype.popToItem = function(itemNumber) {
if (!$(this.itemContainer).parents('.'+this.wrapperClass+' :animated').length) {
$($(this.itemContainer).children("li").get(this.currentItem-1)).hide();
$($(this.itemContainer).children("li").get(itemNumber-1)).show();
this.currentItem = itemNumber;
this.checkControls();
}
};
Slideshow.prototype.moveToItem = function(itemNumber) {
if (!$(this.itemContainer).parents('.'+this.wrapperClass+' :animated').length) {
var lis = $(this.itemContainer).children("li");
$(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);
}
};
Slideshow.prototype.getItemWidth = function () {
return $(this.itemContainer)
.parents('.' + this.wrapperClass)
.width();
};
Slideshow.prototype.popToItem = function (itemNumber) {
if (
!$(this.itemContainer).parents('.' + this.wrapperClass + ' :animated')
.length
) {
$(
$(this.itemContainer)
.children('li')
.get(this.currentItem - 1),
).hide();
$(
$(this.itemContainer)
.children('li')
.get(itemNumber - 1),
).show();
this.currentItem = itemNumber;
this.checkControls();
}
};
Slideshow.prototype.moveToItem = function (itemNumber) {
if (
!$(this.itemContainer).parents('.' + this.wrapperClass + ' :animated')
.length
) {
var lis = $(this.itemContainer).children('li');
$(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
window.DropdownArea = function() {
this.trigger = null;
this.target = '';
this.targetParent = '';
this.callbackFunction = function(){};
this.preventDefault = true;
this.showSpeed = 200;
this.hideSpeed = 200;
this.hideOnBodyClick = true;
};
DropdownArea.prototype.bodyclick = function(e) {
// this will get fired on click of body, we need to close the dropdown
if (this.bodyWatching && this.hideOnBodyClick) {
if (!
($(e.target).get(0) == $(this.targetParent).get(0) ||
$(e.target).parents(this.targetParent).length)
) {
this.hide();
}
}
}
DropdownArea.prototype.hide = function() {
var self = this;
$(self.targetParent).removeClass('expanded');
$(self.target).slideUp(self.hideSpeed, function() {
//unbind bodyclick
self.bodyWatching = false;
});
}
DropdownArea.prototype.show = function() {
var self = this;
$(self.targetParent).addClass('expanded');
$(self.target).slideDown(self.showSpeed, function() {
self.bodyWatching = true;
});
}
DropdownArea.prototype.init = function() {
// slidey dropdown area
window.DropdownArea = function () {
this.trigger = null;
this.target = '';
this.targetParent = '';
this.callbackFunction = function () {};
this.preventDefault = true;
this.showSpeed = 200;
this.hideSpeed = 200;
this.hideOnBodyClick = true;
};
DropdownArea.prototype.bodyclick = function (e) {
// this will get fired on click of body, we need to close the dropdown
if (this.bodyWatching && this.hideOnBodyClick) {
if (
!(
$(e.target).get(0) == $(this.targetParent).get(0) ||
$(e.target).parents(this.targetParent).length
)
) {
this.hide();
}
}
};
DropdownArea.prototype.hide = function () {
var self = this;
$(self.targetParent).removeClass('expanded');
$(self.target).slideUp(self.hideSpeed, function () {
//unbind bodyclick
self.bodyWatching = false;
});
};
DropdownArea.prototype.show = function () {
var self = this;
$(self.targetParent).addClass('expanded');
$(self.target).slideDown(self.showSpeed, function () {
self.bodyWatching = true;
});
};
DropdownArea.prototype.init = function () {
// advanced dropdown
var self = this;
$(this.target).hide();
if (this.trigger) {
this.trigger.click(
function(e) {
if(! $(self.target+':animated').length) {
if ($(self.target+':visible').length){
self.callbackFunction();
self.hide();
} else {
self.callbackFunction();
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);
this.trigger.click(function (e) {
if (!$(self.target + ':animated').length) {
if ($(self.target + ':visible').length) {
self.callbackFunction();
self.hide();
} else {
self.callbackFunction();
self.show();
}
}
HeaderSlideshow.prototype = new Slideshow();
HeaderSlideshow.prototype.moveToItem = function(itemNumber) {
Slideshow.prototype.moveToItem.call(this, itemNumber);
$('.section-teaser .teaser-header li').removeClass('selected');
$('.section-teaser .teaser-header li').eq(itemNumber - 1).addClass('selected');
};
$(self.target).trigger('click');
return !self.preventDefault;
});
// if box now showing bind bodyclick
$('body').on('click', function (e) {
self.bodyclick(e);
});
}
};
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();
//Move the list of promo categories below the controls to allow all content to expand
$('.teaser-header').insertBefore(".slideshow-controls");
var headerListItems = $('.section-teaser .teaser-header li a');
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;
// 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();
HeaderSlideshow.prototype.moveToItem = function (itemNumber) {
Slideshow.prototype.moveToItem.call(this, itemNumber);
$('.section-teaser .teaser-header li').removeClass('selected');
$('.section-teaser .teaser-header li')
.eq(itemNumber - 1)
.addClass('selected');
};
})(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($) {
// Greys out the favorites icon when it is clicked
$(".item-info li.favorite").click(function () {
var self = this;
$(self).addClass("favorite-loading");
setTimeout(function() {
$(self).addClass("favorite-added");
},2000);
//Move the list of promo categories below the controls to allow all content to expand
$('.teaser-header').insertBefore('.slideshow-controls');
var headerListItems = $('.section-teaser .teaser-header li a');
headerListItems.click(function () {
headerListItems.parent('li').removeClass('selected');
$(this).parent('li').addClass('selected');
homepageSlider.moveToItem(headerListItems.index(this) + 1);
homepageSlider.scroll = false;
return false;
});
// account dropdown in auxillary menu
var accountDropdown = new DropdownArea();
// set up variables for object
accountDropdown.trigger = ($('ul.account .controller')); // node
accountDropdown.target = ('ul.account ul'); // reference
accountDropdown.targetParent = ('ul.account'); // reference
accountDropdown.init();
return homepageSlider;
};
})(jQuery);
// tools dropdown in auxillary menu
var toolsDropdown = new DropdownArea();
// set up variables for object
toolsDropdown.trigger = ($('ul.tools .controller')); // node
toolsDropdown.target = ('ul.tools ul'); // reference
toolsDropdown.targetParent = ('ul.tools'); // reference
toolsDropdown.init();
jQuery(function ($) {
// Greys out the favorites icon when it is clicked
$('.item-info li.favorite').click(function () {
var self = this;
$(self).addClass('favorite-loading');
setTimeout(function () {
$(self).addClass('favorite-added');
}, 2000);
});
// change dropdown in auxillary menu
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();
// account dropdown in auxillary menu
var accountDropdown = new DropdownArea();
// set up variables for object
accountDropdown.trigger = $('ul.account .controller'); // node
accountDropdown.target = 'ul.account ul'; // reference
accountDropdown.targetParent = 'ul.account'; // reference
accountDropdown.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;
})
// tools dropdown in auxillary menu
var toolsDropdown = new DropdownArea();
// set up variables for object
toolsDropdown.trigger = $('ul.tools .controller'); // node
toolsDropdown.target = 'ul.tools ul'; // reference
toolsDropdown.targetParent = 'ul.tools'; // reference
toolsDropdown.init();
// change dropdown in auxillary menu
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. */
$(".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');
function initBanners() {
var $body = $(document.body);
var $body = $(document.body);
if ($body.hasClass('editor-tools')) {
// 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.
return;
}
if ($body.hasClass('editor-tools')) {
// 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.
return;
}
// Show the various banners, but only one at a time, and only if they
// haven't been dimissed before.
// To reset dismissal state: z.visitor.remove('xx')
// Show the various banners, but only one at a time, and only if they
// haven't been dimissed before.
// To reset dismissal state: z.visitor.remove('xx')
// Show the bad-browser message
if (!z.visitor.get('seen_badbrowser_warning') && $body.hasClass('badbrowser')) {
$('#site-nonfx').show();
}
// Show the first visit banner.
else if (!z.visitor.get('seen_impala_first_visit')) {
$body.addClass('firstvisit');
z.visitor.set('seen_impala_first_visit', 1);
}
// Show the link to try the new frontend (only on the homepage for now).
else if (!z.visitor.get('seen_try_new_frontend') && $body.hasClass('home')) {
$('#try-new-frontend').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();
}
// Show the bad-browser message
if (
!z.visitor.get('seen_badbrowser_warning') &&
$body.hasClass('badbrowser')
) {
$('#site-nonfx').show();
}
// Show the first visit banner.
else if (!z.visitor.get('seen_impala_first_visit')) {
$body.addClass('firstvisit');
z.visitor.set('seen_impala_first_visit', 1);
}
// Show the link to try the new frontend (only on the homepage for now).
else if (!z.visitor.get('seen_try_new_frontend') && $body.hasClass('home')) {
$('#try-new-frontend').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.
$body.on('click', '.site-balloon .close, .site-tip .close', _pd(function() {
var $parent = $(this).closest('.site-balloon, .site-tip');
$parent.fadeOut();
if ($parent.is('#site-nonfx')) {
z.visitor.set('seen_badbrowser_warning', 1);
} else if ($parent.is('#acr-pitch')) {
z.visitor.set('seen_acr_pitch', 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);
}
}));
// Allow dismissal of site-balloons.
$body.on(
'click',
'.site-balloon .close, .site-tip .close',
_pd(function () {
var $parent = $(this).closest('.site-balloon, .site-tip');
$parent.fadeOut();
if ($parent.is('#site-nonfx')) {
z.visitor.set('seen_badbrowser_warning', 1);
} else if ($parent.is('#acr-pitch')) {
z.visitor.set('seen_acr_pitch', 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.
z.keys = {
'SHIFT': 16,
'CONTROL': 17,
'ALT': 18,
'PAUSE': 19,
'CAPS_LOCK': 20,
'ESCAPE': 27,
'ENTER': 13,
'PAGE_UP': 33,
'PAGE_DOWN': 34,
'LEFT': 37,
'UP': 38,
'RIGHT': 39,
'DOWN': 40,
'HOME': 36,
'END': 35,
'COMMAND': 91,
'WINDOWS_RIGHT': 92,
'COMMAND_RIGHT': 93,
'WINDOWS_LEFT_OPERA': 219,
'WINDOWS_RIGHT_OPERA': 220,
'APPLE': 24
SHIFT: 16,
CONTROL: 17,
ALT: 18,
PAUSE: 19,
CAPS_LOCK: 20,
ESCAPE: 27,
ENTER: 13,
PAGE_UP: 33,
PAGE_DOWN: 34,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
HOME: 36,
END: 35,
COMMAND: 91,
WINDOWS_RIGHT: 92,
COMMAND_RIGHT: 93,
WINDOWS_LEFT_OPERA: 219,
WINDOWS_RIGHT_OPERA: 220,
APPLE: 24,
};

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

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

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

@ -1,61 +1,70 @@
// Replaces rating selectboxes with the rating widget
$.fn.ratingwidget = function(classes) {
this.each(function(n, el) {
if (!classes) {
classes = '';
$.fn.ratingwidget = function (classes) {
this.each(function (n, el) {
if (!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,
$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);
})
.mouseover(function (evt) {
var t = $(evt.target);
if (t.attr('data-stars')) {
showStars(t.attr('data-stars'));
}
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);
}
}
}).mouseover(function(evt) {
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;
})
.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 */
function fileSizeFormat(bytes) {
var s = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
if(bytes === 0) return bytes + " " + s[1];
var e = Math.floor( Math.log(bytes) / Math.log(1024) );
return (bytes / Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e];
var s = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
if (bytes === 0) return bytes + ' ' + s[1];
var e = Math.floor(Math.log(bytes) / Math.log(1024));
return (bytes / Math.pow(1024, Math.floor(e))).toFixed(2) + ' ' + s[e];
}
(function($) {
function getErrors(results) {
return results.errors;
}
(function ($) {
function getErrors(results) {
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(){
var $upload_field = $(this),
formData = false,
errors = false,
aborted = false;
if (options) {
$.extend(settings, options);
}
if (options) {
$.extend( settings, options );
}
$upload_field.on('change', uploaderStart);
$upload_field.on("change", uploaderStart);
$(settings.cancel).click(
_pd(function () {
$upload_field.trigger('upload_action_abort');
}),
);
$(settings.cancel).click(_pd(function(){
$upload_field.trigger('upload_action_abort');
}));
function uploaderStart() {
if ($upload_field[0].files.length === 0) {
return;
}
function uploaderStart() {
if($upload_field[0].files.length === 0) {
return;
}
var domfile = $upload_field[0].files[0],
url = $upload_field.attr('data-upload-url'),
csrf = $('input[name=csrfmiddlewaretoken]').val(),
file = {
name: domfile.name || domfile.fileName,
size: domfile.size,
type: domfile.type,
};
var domfile = $upload_field[0].files[0],
url = $upload_field.attr('data-upload-url'),
csrf = $("input[name=csrfmiddlewaretoken]").val(),
file = {'name': domfile.name || domfile.fileName,
'size': domfile.size,
'type': domfile.type};
formData = new z.FormData();
aborted = false;
formData = new z.FormData();
aborted = false;
$upload_field.trigger('upload_start', [file]);
$upload_field.trigger("upload_start", [file]);
/* Disable uploading while something is uploading */
$upload_field.prop('disabled', true);
$upload_field.parent().find('a').addClass("disabled");
$upload_field.on("reenable_uploader", function() {
$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();
}
/* Disable uploading while something is uploading */
$upload_field.prop('disabled', true);
$upload_field.parent().find('a').addClass('disabled');
$upload_field.on('reenable_uploader', function () {
$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();
}
});
};
})(jQuery);

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

@ -34,117 +34,122 @@
file in the upload box when the "onchange" event is fired.]
*/
// Get an object URL across browsers.
$.fn.objectUrl = function(offset) {
var files = $(this)[0].files,
url = false;
if (z.capabilities.fileAPI && files.length) {
offset = offset || 0;
var f = files[offset];
if (typeof window.URL !== 'undefined') {
url = window.URL.createObjectURL(f);
} else if (typeof window.webkitURL == 'function') {
url = window.webkitURL.createObjectURL(f);
} else if(typeof f.getAsDataURL == 'function') {
url = f.getAsDataURL();
}
$.fn.objectUrl = function (offset) {
var files = $(this)[0].files,
url = false;
if (z.capabilities.fileAPI && files.length) {
offset = offset || 0;
var f = files[offset];
if (typeof window.URL !== 'undefined') {
url = window.URL.createObjectURL(f);
} else if (typeof window.webkitURL == 'function') {
url = window.webkitURL.createObjectURL(f);
} else if (typeof f.getAsDataURL == 'function') {
url = f.getAsDataURL();
}
return url;
}
return url;
};
(function ($) {
var instance_id = 0;
(function($) {
var instance_id = 0;
$.fn.imageUploader = function () {
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() {
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();
// No files? No API support? No shirt? No service.
if (!z.capabilities.fileAPI || files.length === 0) {
return false;
}
// No files? No API support? No shirt? No service.
if (!z.capabilities.fileAPI || files.length === 0) {
return false;
$upload_field.trigger('upload_start_all');
// 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.
$.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();
// And we're off!
$upload_field.trigger('upload_start', [file]);
instance_id++;
outstanding_uploads++;
// Set things up
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) {
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;
}
// 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;
}
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!
$upload_field.trigger("upload_start", [file]);
// Actually do the sending.
formData.send();
});
// Set things up
formData.open("POST", url, true);
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("");
};
// Clear out images, since we uploaded them.
$upload_field.val('');
};
})(jQuery);

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

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

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

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

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

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

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

@ -1,166 +1,178 @@
$(function () {
if (!$("body").hasClass('addon-details')) return;
if (!$('body').hasClass('addon-details')) return;
if ($("body.restyle").length === 1) {
$('#background-wrapper').height(
$('.amo-header').height() +
($('.notification-box').length ? 80 : 0) +
$('.addon-description-header').height() + 20
);
if ($('body.restyle').length === 1) {
$('#background-wrapper').height(
$('.amo-header').height() +
($('.notification-box').length ? 80 : 0) +
$('.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);
}
$(".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() {
$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);
}
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 ($('#review-add-box').exists())
$('#review-add-box').modal('#add-review', { delegate: '#page', width: '650px' });
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();
if ($('#privacy-policy').exists())
$('#privacy-policy').modal('.privacy-policy', { width: '500px' });
if ($('#webext-permissions').exists())
$('#webext-permissions').modal('.webext-permissions', { width: '500px' });
// 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;
}
$('#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) {
return JSON.stringify(a) == JSON.stringify(b);
return JSON.stringify(a) == JSON.stringify(b);
}
z._AjaxCache = {};
z.AjaxCache = (function() {
return function(namespace) {
if (z._AjaxCache[namespace] === undefined) {
z._AjaxCache[namespace] = {
'previous': {'args': '', 'data': ''},
'items': {}
};
}
return z._AjaxCache[namespace];
};
z.AjaxCache = (function () {
return function (namespace) {
if (z._AjaxCache[namespace] === undefined) {
z._AjaxCache[namespace] = {
previous: { args: '', data: '' },
items: {},
};
}
return z._AjaxCache[namespace];
};
})();
(function($) {
$.ajaxCache = function(o) {
o = $.extend({
(function ($) {
$.ajaxCache = function (o) {
o = $.extend(
{
url: '',
type: 'get',
data: {}, // Key/value pairs of form data.
newItems: $.noop, // Callback upon success of items fetched.
cacheSuccess: $.noop, // Callback upon success of items fetched
// in cache.
ajaxSuccess: $.noop, // Callback upon success of Ajax request.
ajaxFailure: $.noop // Callback upon failure of Ajax request.
}, o);
data: {}, // Key/value pairs of form data.
newItems: $.noop, // Callback upon success of items fetched.
cacheSuccess: $.noop, // Callback upon success of items fetched
// in cache.
ajaxSuccess: $.noop, // Callback upon success of Ajax request.
ajaxFailure: $.noop, // Callback upon failure of Ajax request.
},
o,
);
if (!z.capabilities.JSON || parseFloat(jQuery.fn.jquery) < 1.5) {
// 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
// sooner than later.
return $.ajax({
url: o.url,
type: o.method,
data: o.data,
success: function(data) {
o.newItems(data, data);
o.ajaxSuccess(data, items);
},
errors: function(data) {
o.ajaxFailure(data);
}
});
// 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
// sooner than later.
return $.ajax({
url: o.url,
type: o.method,
data: o.data,
success: function (data) {
o.newItems(data, data);
o.ajaxSuccess(data, items);
},
errors: function (data) {
o.ajaxFailure(data);
},
});
}
var cache = z.AjaxCache(o.url + ':' + o.type),
args = JSON.stringify(o.data),
previous_args = JSON.stringify(cache.previous.args),
items,
request;
args = JSON.stringify(o.data),
previous_args = JSON.stringify(cache.previous.args),
items,
request;
if (args != previous_args) {
if (!!cache.items[args]) {
items = cache.items[args];
if (o.newItems) {
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 (!!cache.items[args]) {
items = cache.items[args];
if (o.newItems) {
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);
});
}
}
}
return request;
};
};
})(jQuery);

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

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

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

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

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

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

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

@ -1,31 +1,27 @@
function clearErrors(context) {
$('.errorlist', context).remove();
$('.error', context).removeClass('error');
$('.errorlist', context).remove();
$('.error', context).removeClass('error');
}
function populateErrors(context, o) {
clearErrors(context);
var $list = $('<ul class="errorlist"></ul>');
$.each(o, function(i, v) {
var $row = $('[name=' + i + ']', context).closest('.row');
$row.addClass('error');
$row.append($list.append($(format('<li>{0}</li>', _.escape(v)))));
});
clearErrors(context);
var $list = $('<ul class="errorlist"></ul>');
$.each(o, function (i, v) {
var $row = $('[name=' + i + ']', context).closest('.row');
$row.addClass('error');
$row.append($list.append($(format('<li>{0}</li>', _.escape(v)))));
});
}
function fieldFocused(e) {
var tags = /input|keygen|meter|option|output|progress|select|textarea/i;
return tags.test(e.target.nodeName);
var tags = /input|keygen|meter|option|output|progress|select|textarea/i;
return tags.test(e.target.nodeName);
}
function postUnsaved(data) {
$('input[name="unsaved_data"]').val(JSON.stringify(data));
$('input[name="unsaved_data"]').val(JSON.stringify(data));
}
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) {
var $totalForms = $('#id_' + prefix + '-TOTAL_FORMS'),
$maxForms = $('#id_' + prefix + '-MAX_NUM_FORMS'),
inc = inc || 1,
num = parseInt($totalForms.val(), 10) + inc;
if ($maxForms.length && $maxForms.val().length) {
var maxNum = parseInt($maxForms.val(), 10);
if (num > maxNum) {
return num - 1;
}
var $totalForms = $('#id_' + prefix + '-TOTAL_FORMS'),
$maxForms = $('#id_' + prefix + '-MAX_NUM_FORMS'),
inc = inc || 1,
num = parseInt($totalForms.val(), 10) + inc;
if ($maxForms.length && $maxForms.val().length) {
var maxNum = parseInt($maxForms.val(), 10);
if (num > maxNum) {
return num - 1;
}
$totalForms.val(num);
return num;
}
$totalForms.val(num);
return num;
}
/**
* zAutoFormset: handles Django formsets with autocompletion like a champ!
* 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({
delegate: document.body, // Delegate (probably some nearby parent).
extraForm: '.extra-form', // Selector for element that contains the
// 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
// HTML for extra-form template.
prefix: 'form', // Formset prefix (Django default: 'form').
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
// that will contain the value of the
// formPK for each newly added form.
formSelector: 'li', // Selector for each form container.
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.
removedCB: null, // Callback for each form removed.
autocomplete: null // Custom handler you can provide to handle
// autocompletion yourself.
}, o);
autocomplete: null, // Custom handler you can provide to handle
// autocompletion yourself.
},
o,
);
var $delegate = $(o.delegate),
$forms = o.forms ? $delegate.find(o.forms) : $delegate,
$extraForm = $delegate.find(o.extraForm),
formsetPrefix = o.prefix,
hiddenField = o.hiddenField,
removeClass = o.removeClass,
formSelector = o.formSelector,
formPK = o.formPK,
src = o.src || $delegate.attr('data-src'),
$input = o.input ? $(o.input) : $delegate.find('input.autocomplete'),
searchField = o.searchField,
minLength = o.minSearchLength,
$maxForms = $('#id_' + formsetPrefix + '-MAX_NUM_FORMS'),
width = o.width,
addedCB = o.addedCB,
removedCB = o.removedCB,
autocomplete = o.autocomplete,
maxItems;
$forms = o.forms ? $delegate.find(o.forms) : $delegate,
$extraForm = $delegate.find(o.extraForm),
formsetPrefix = o.prefix,
hiddenField = o.hiddenField,
removeClass = o.removeClass,
formSelector = o.formSelector,
formPK = o.formPK,
src = o.src || $delegate.attr('data-src'),
$input = o.input ? $(o.input) : $delegate.find('input.autocomplete'),
searchField = o.searchField,
minLength = o.minSearchLength,
$maxForms = $('#id_' + formsetPrefix + '-MAX_NUM_FORMS'),
width = o.width,
addedCB = o.addedCB,
removedCB = o.removedCB,
autocomplete = o.autocomplete,
maxItems;
if ($maxForms.length && $maxForms.val()) {
maxItems = parseInt($maxForms.val(), 10);
} else if (o.maxForms) {
maxItems = o.maxForms;
}
if ($maxForms.length && $maxForms.val()) {
maxItems = parseInt($maxForms.val(), 10);
} else if (o.maxForms) {
maxItems = o.maxForms;
}
function findItem(item) {
if (item) {
var $item = $forms.find('[name$=-' + hiddenField + '][value=' + item[formPK] + ']');
if ($item.length) {
var $f = $item.closest(formSelector);
return {'exists': true, 'visible': $f.is(':visible'), 'item': $f};
}
if (item) {
var $item = $forms.find(
'[name$=-' + hiddenField + '][value=' + item[formPK] + ']',
);
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() {
$input.val('');
$input.removeAttr('data-item');
toggleInput();
$input.val('');
$input.removeAttr('data-item');
toggleInput();
}
function toggleInput() {
if (!maxItems) {
return;
}
var $visible = $forms.find(formSelector + ':visible').length;
if ($visible >= maxItems) {
$input.prop('disabled', true).slideUp();
$('.ui-autocomplete').hide();
} else if ($visible < maxItems) {
$input.filter(':disabled').prop('disabled', false).slideDown();
}
if (!maxItems) {
return;
}
var $visible = $forms.find(formSelector + ':visible').length;
if ($visible >= maxItems) {
$input.prop('disabled', true).slideUp();
$('.ui-autocomplete').hide();
} else if ($visible < maxItems) {
$input.filter(':disabled').prop('disabled', false).slideDown();
}
}
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.
var dupe = findItem(item);
if (dupe.exists) {
if (!dupe.visible) {
// Undelete the item.
var $item = dupe.item;
$item.find('input[name$=-DELETE]').prop('checked', false);
$item.slideDown(toggleInput);
}
clearInput();
return;
// Check if this item has already been added.
var dupe = findItem(item);
if (dupe.exists) {
if (!dupe.visible) {
// Undelete the item.
var $item = dupe.item;
$item.find('input[name$=-DELETE]').prop('checked', false);
$item.slideDown(toggleInput);
}
clearInput();
return;
}
var formId = updateTotalForms(formsetPrefix, 1) - 1,
emptyForm = $extraForm.html().replace(/__prefix__/g, formId);
clearInput();
var $f;
if (addedCB) {
$f = addedCB(emptyForm, item);
} else {
$f = $(f);
}
var formId = updateTotalForms(formsetPrefix, 1) - 1,
emptyForm = $extraForm.html().replace(/__prefix__/g, formId);
$f.hide().appendTo($forms).slideDown(toggleInput);
var $f;
if (addedCB) {
$f = addedCB(emptyForm, item);
} else {
$f = $(f);
}
// Update hidden field.
$forms.find(formSelector + ':last [name$=-' + hiddenField + ']')
.val(item[formPK]);
$f.hide().appendTo($forms).slideDown(toggleInput);
// Update hidden field.
$forms
.find(formSelector + ':last [name$=-' + hiddenField + ']')
.val(item[formPK]);
}
function removed(el) {
el.slideUp(toggleInput);
// Mark as deleted.
el.find('input[name$=-DELETE]').prop('checked', true);
el.slideUp(toggleInput);
// Mark as deleted.
el.find('input[name$=-DELETE]').prop('checked', true);
if (removedCB) {
removedCB(el);
}
if (removedCB) {
removedCB(el);
}
// If this was not an initial form (i.e., an extra form), delete the
// form and decrement the TOTAL_FORMS count.
if (!el.find('input[name$=-' + formPK + ']').length) {
el.remove();
updateTotalForms(formsetPrefix, -1);
}
// If this was not an initial form (i.e., an extra form), delete the
// form and decrement the TOTAL_FORMS count.
if (!el.find('input[name$=-' + formPK + ']').length) {
el.remove();
updateTotalForms(formsetPrefix, -1);
}
}
function _renderItem(ul, item) {
if (!findItem(item).visible) {
var $a = $(format('<a><img src="{0}" alt="">{1}</a>',
[item.icons['32'], _.escape(item.name)]));
return $('<li>').data('item.autocomplete', item)
.append($a).appendTo(ul);
}
if (!findItem(item).visible) {
var $a = $(
format('<a><img src="{0}" alt="">{1}</a>', [
item.icons['32'],
_.escape(item.name),
]),
);
return $('<li>')
.data('item.autocomplete', item)
.append($a)
.appendTo(ul);
}
}
function _renderItemData(ul, item) {
var rendered = _renderItem( ul, item );
var rendered = _renderItem(ul, item);
// We are overwriting `_renderItem` in some places and return
// nothing in case of duplicate filtering.
if (rendered) {
rendered.data("ui-autocomplete-item", item);
}
// We are overwriting `_renderItem` in some places and return
// nothing in case of duplicate filtering.
if (rendered) {
rendered.data('ui-autocomplete-item', item);
}
}
if (autocomplete) {
autocomplete();
autocomplete();
} else {
$input.autocomplete({
minLength: minLength,
width: width,
source: function(request, response) {
var d = {};
d[searchField] = request.term;
$.getJSON(src, d, response);
},
focus: function(event, ui) {
event.preventDefault();
$input.val(ui.item.name);
},
select: function(event, ui) {
event.preventDefault();
if (ui.item) {
$input.val(ui.item.name);
$input.attr('data-item', JSON.stringify(ui.item));
added();
}
$input
.autocomplete({
minLength: minLength,
width: width,
source: function (request, response) {
var d = {};
d[searchField] = request.term;
$.getJSON(src, d, response);
},
focus: function (event, ui) {
event.preventDefault();
$input.val(ui.item.name);
},
select: function (event, ui) {
event.preventDefault();
if (ui.item) {
$input.val(ui.item.name);
$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.
$.each(items, function(index, item) {
_renderItemData(ul, item);
});
};
},
})
.data('ui-autocomplete')._renderMenu = function (ul, items) {
// Overwrite _renderMenu to patch in our custom `_renderItemData`
// and `_renderItem` to allow for our custom list-filter.
$.each(items, function (index, item) {
_renderItemData(ul, item);
});
};
}
toggleInput();
$delegate.on('click', '.' + removeClass, _pd(function() {
$delegate.on(
'click',
'.' + removeClass,
_pd(function () {
removed($(this).closest(formSelector));
}));
};
}),
);
};
})(jQuery);

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

@ -1,151 +1,175 @@
//bind pager controls to our addon grids
$('.island .listing-grid').on('grid.init', function(e, data) {
var $grid = data.self,
numPages = data.maxPage;
$('.island .listing-grid').on('grid.init', function (e, data) {
var $grid = data.self,
numPages = data.maxPage;
if (numPages > 0) {
var $nav = $('<nav class="pager">');
$nav.append('<a href="#" class="prev">&laquo;</a>');
for (var i=0; i<=numPages; i++) {
$nav.append('<a href="#" class="' + (i==0 ? 'selected ': '') + 'dot"><b></b></a>');
}
$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');
});
if (numPages > 0) {
var $nav = $('<nav class="pager">');
$nav.append('<a href="#" class="prev">&laquo;</a>');
for (var i = 0; i <= numPages; i++) {
$nav.append(
'<a href="#" class="' +
(i == 0 ? 'selected ' : '') +
'dot"><b></b></a>',
);
}
$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) {
var $grid = $(grid);
if ($grid.hasClass('hovercard')) {
$grid = $grid.parent();
}
$grid.find('.hovercard h3').truncate();
$grid.on('mouseover', '.hovercard', function() {
var $el = $(this);
setTimeout(function() {
$el.find('h3').untruncate();
}, 100);
}).on('mouseout', '.hovercard', function() {
var $el = $(this);
setTimeout(function() {
$el.find('h3').truncate();
}, 100);
var $grid = $(grid);
if ($grid.hasClass('hovercard')) {
$grid = $grid.parent();
}
$grid.find('.hovercard h3').truncate();
$grid
.on('mouseover', '.hovercard', function () {
var $el = $(this);
setTimeout(function () {
$el.find('h3').untruncate();
}, 100);
})
.on('mouseout', '.hovercard', function () {
var $el = $(this);
setTimeout(function () {
$el.find('h3').truncate();
}, 100);
});
}
function listing_grid() {
var $grid = $(this),
$pages = $grid.find('section'),
current = 0,
maxPage = $pages.length-1;
$grid.trigger("grid.init", {self: $grid, current: current, maxPage: maxPage});
$grid.go = function(n) {
if (n != current) {
n = n < 0 ? 0 : (n > maxPage ? maxPage : n);
current = n;
$pages.hide().eq(n).show().find('.hovercard h3').truncate();
$grid.trigger("grid.update", {self: $grid, current: current, maxPage: maxPage});
}
};
$grid.prevPage = function() {
$grid.go(current-1);
};
$grid.nextPage = function() {
$grid.go(current+1);
};
hoverTruncate(this);
$grid.css({
'width': $grid.width() + 'px',
'height': $grid.height() + 'px'
});
return $grid;
var $grid = $(this),
$pages = $grid.find('section'),
current = 0,
maxPage = $pages.length - 1;
$grid.trigger('grid.init', {
self: $grid,
current: current,
maxPage: maxPage,
});
$grid.go = function (n) {
if (n != current) {
n = n < 0 ? 0 : n > maxPage ? maxPage : n;
current = n;
$pages.hide().eq(n).show().find('.hovercard h3').truncate();
$grid.trigger('grid.update', {
self: $grid,
current: current,
maxPage: maxPage,
});
}
};
$grid.prevPage = function () {
$grid.go(current - 1);
};
$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() {
"use strict";
// Paginate listing grids.
$('.listing-grid').each(listing_grid);
// Paginate listing grids.
$('.listing-grid').each(listing_grid);
// Truncate titles on single hovercards.
$('.hovercard').each(function () {
hoverTruncate(this);
});
// Truncate titles on single hovercards.
$('.hovercard').each(function() {
hoverTruncate(this);
});
// load deferred images.
$('img[data-defer-src]').each(function () {
var $img = $(this);
$img.attr('src', $img.attr('data-defer-src'));
});
// load deferred images.
$('img[data-defer-src]').each(function() {
var $img = $(this);
$img.attr('src', $img.attr('data-defer-src'));
});
// Email obfuscation.
$('span.emaillink').each(function () {
var $this = $(this);
$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.
$('span.emaillink').each(function() {
var $this = $(this);
$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');
});
$('#page').on(
'click',
'.expando .toggle',
_pd(function () {
$(this).closest('.expando').toggleClass('expanded');
}),
);
$('#page').on('click', '.expando .toggle', _pd(function() {
$(this).closest('.expando').toggleClass('expanded');
}));
var fragment = window.location.hash;
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;
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');
$('#page').on('click', '.scrollto', function (e) {
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);
});
$('#page').on('click', '.scrollto', function(e) {
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();
$("select[name='rating']").ratingwidget();
});
// AJAX form submit
$(function() {
$(document).on('submit', 'form.ajax-submit, .ajax-submit form', function() {
var $form = $(this),
$parent = $form.is('.ajax-submit') ? $form : $form.closest('.ajax-submit'),
params = $form.serializeArray();
$(function () {
$(document).on('submit', 'form.ajax-submit, .ajax-submit form', function () {
var $form = $(this),
$parent = $form.is('.ajax-submit')
? $form
: $form.closest('.ajax-submit'),
params = $form.serializeArray();
$form.find('.submit, button[type=submit], submit').prop('disabled', true).addClass('loading-submit');
$.post($form.attr('action'), params, function(d) {
var $replacement = $(d);
$parent.replaceWith($replacement);
$replacement.trigger('ajax-submit-loaded');
$replacement.find('.ajax-submit').trigger('ajax-submit-loaded');
});
return false;
$form
.find('.submit, button[type=submit], submit')
.prop('disabled', true)
.addClass('loading-submit');
$.post($form.attr('action'), params, function (d) {
var $replacement = $(d);
$parent.replaceWith($replacement);
$replacement.trigger('ajax-submit-loaded');
$replacement.find('.ajax-submit').trigger('ajax-submit-loaded');
});
return false;
});
});

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

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

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

@ -11,23 +11,23 @@
*
*/
(function() {
var $window = $(window);
$window.on('click', '.install-button a.button', function(e) {
e.preventDefault();
var $el = $(this);
(function () {
var $window = $(window);
$window.on('click', '.install-button a.button', function (e) {
e.preventDefault();
var $el = $(this);
// When everything is loaded, trigger a click on the button
$window.on('buttons_loaded_click', function() {
$el.trigger('click');
});
// When everything is loaded, trigger a click on the button
$window.on('buttons_loaded_click', function () {
$el.trigger('click');
});
$window.on('buttons_loaded', function() {
// Trigger all the clicks
$window.trigger('buttons_loaded_click');
});
$window.on('buttons_loaded', function () {
// Trigger all the clicks
$window.trigger('buttons_loaded_click');
// Clean up after ourselves
$window.off('buttons_loaded buttons_loaded_click');
$window.off('click', '.install-button a.button');
});
// Clean up after ourselves
$window.off('buttons_loaded buttons_loaded_click');
$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(),
$window = $(window);
$('.review-reason').popup('.flag-review', {
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', {
delegate: $(document.body),
width: 'inherit',
callback: function(obj) {
var ct = $(obj.click_target),
$popup = this;
//reset our event handlers
$popup.hideMe();
function addFlag(flag, note) {
$.ajax({type: 'POST',
url: ct.attr('href'),
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'));
function addFlag(flag, note) {
$.ajax({
type: 'POST',
url: ct.attr('href'),
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',
});
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) {
$('#review-add-box form')
.append('<input type="hidden" name="detailed" value="1">').submit();
}));
$('#detail-review-link').click(
_pd(function (e) {
$('#review-add-box form')
.append('<input type="hidden" name="detailed" value="1">')
.submit();
}),
);
});

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

@ -1,178 +1,184 @@
(function () {
var appver_input = $('#id_appver');
var platform_input = $('#id_platform');
var appver_input = $('#id_appver');
var platform_input = $('#id_platform');
function autofillPlatform(context) {
var $context = $(context || document.body);
function autofillPlatform(context) {
var $context = $(context || document.body);
$('#search', $context).on('autofill', function(e) {
var $this = $(this);
$('#search', $context)
.on('autofill', function (e) {
var $this = $(this);
// Bail if search is present but not the appver input somehow.
if (!appver_input.length) {
return;
}
// Bail if search is present but not the appver input somehow.
if (!appver_input.length) {
return;
}
// Populate search form with browser version and OS.
var gv = z.getVars();
// Populate search form with browser version and OS.
var gv = z.getVars();
// Facets are either the ones defined in the URL, or the detected
// browser version and platform.
if (!!(gv.appver)) { // Defined in URL parameter
appver_input.val(gv.appver);
} else if (z.appMatchesUserAgent) { // Fallback to detected
// Only do this if firefox 57 or higher. Lower versions default
// to searching for all add-ons even if they might be
// incompatible. https://github.com/mozilla/addons-server/issues/5482
if (VersionCompare.compareVersions(z.browserVersion, '57.0') >= 0) {
appver_input.val(z.browserVersion);
}
}
// Facets are either the ones defined in the URL, or the detected
// browser version and platform.
if (!!gv.appver) {
// Defined in URL parameter
appver_input.val(gv.appver);
} else if (z.appMatchesUserAgent) {
// Fallback to detected
// Only do this if firefox 57 or higher. Lower versions default
// to searching for all add-ons even if they might be
// 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
platform_input.val(gv.platform);
} else if (z.appMatchesUserAgent) { // Fallback to detected
platform_input.val(z.platform);
}
}).trigger('autofill');
if (!!gv.platform) {
// Defined in URL parameter
platform_input.val(gv.platform);
} else if (z.appMatchesUserAgent) {
// Fallback to detected
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() {
$('#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);
function pjaxOpen(url) {
var urlBase = location.pathname + location.search;
if (!!url && url != '#' && url != urlBase) {
$.pjax({
url: url,
container: container,
timeout: 5000,
});
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) {
var $container = $(this),
container = containerSelector,
$triggered;
function loading() {
var $wrapper = $container.closest('.results'),
msg = gettext('Updating results&hellip;'),
cls = 'updating';
$wrapper.addClass('loading');
function pjaxOpen(url) {
var urlBase = location.pathname + location.search;
if (!!url && url != '#' && url != urlBase) {
$.pjax({
url: url,
container: container,
timeout: 5000
});
}
// 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'));
}
}
function hijackLink() {
$triggered = $(this);
pjaxOpen($triggered.attr('href'));
}
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));
};
$(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) {
if (typeof qs === 'undefined') {
qs = location.search;
}
if (qs && qs[0] == '?') {
qs = qs.substr(1); // Filter off the leading ? if it's there.
}
if (!qs) return {};
z.getVars = function (qs, excl_undefined) {
if (typeof qs === 'undefined') {
qs = location.search;
}
if (qs && qs[0] == '?') {
qs = qs.substr(1); // Filter off the leading ? if it's there.
}
if (!qs) return {};
return _.chain(qs.split('&')) // ['a=b', 'c=d']
.map(function(c) {return _.map(c.split('='), escape_);}) // [['a', 'b'], ['c', 'd']]
.filter(function(p) { // [['a', 'b'], ['c', undefined]] -> [['a', 'b']]
return !!p[0] && (!excl_undefined || !_.isUndefined(p[1]));
}).object() // {'a': 'b', 'c': 'd'}
.value();
return _.chain(qs.split('&')) // ['a=b', 'c=d']
.map(function (c) {
return _.map(c.split('='), escape_);
}) // [['a', 'b'], ['c', 'd']]
.filter(function (p) {
// [['a', 'b'], ['c', undefined]] -> [['a', 'b']]
return !!p[0] && (!excl_undefined || !_.isUndefined(p[1]));
})
.object() // {'a': 'b', 'c': 'd'}
.value();
};
JSON.parseNonNull = function(text) {
return JSON.parse(text, function(key, value) {
if (typeof value === 'object' && value === null) {
return '';
}
return value;
});
JSON.parseNonNull = function (text) {
return JSON.parse(text, function (key, value) {
if (typeof value === 'object' && value === null) {
return '';
}
return value;
});
};

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

@ -1,63 +1,65 @@
// Init site search suggestions and populate the suggestions container.
(function() {
// AMO search init.
$('#search #search-q').searchSuggestions($('#site-search-suggestions'),
processResults, 'AMO');
(function () {
// AMO search init.
$('#search #search-q').searchSuggestions(
$('#site-search-suggestions'),
processResults,
'AMO',
);
function processResults(settings) {
if (!settings || !settings.category) {
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]);
}
});
function processResults(settings) {
if (!settings || !settings.category) {
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]);
},
});
}
})();

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

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

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

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

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

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

@ -1,43 +1,47 @@
// Web Worker Pool
// size is the max number of arguments
function WorkerPool(size) {
var workers = 0,
jobs = [];
var workers = 0,
jobs = [];
// url: the url of the worker's js
// msg: the initial message to pass to the worker
// cb : the callback to recieve messages from postMessage.
// return true from cb to dismiss the worker and advance the queue.
// ctx: the context for cb.apply
this.queueJob = function(url, msg, cb, ctx) {
var job = {
"url": url,
"msg": msg,
"cb" : cb,
"ctx": ctx
};
jobs.push(job);
if (workers < size) nextJob();
// url: the url of the worker's js
// msg: the initial message to pass to the worker
// cb : the callback to recieve messages from postMessage.
// return true from cb to dismiss the worker and advance the queue.
// ctx: the context for cb.apply
this.queueJob = function (url, msg, cb, ctx) {
var job = {
url: url,
msg: msg,
cb: cb,
ctx: ctx,
};
jobs.push(job);
if (workers < size) nextJob();
};
function nextJob() {
if (jobs.length) {
(function() {
var job = jobs.shift(),
worker = new Worker(job.url);
workers++;
worker.addEventListener('message', function(e) {
if (job.cb.call(job.ctx, e.data, worker)) {
worker.terminate();
worker = null;
workers--;
nextJob();
};
}, false);
worker.postMessage(job.msg);
})();
}
function nextJob() {
if (jobs.length) {
(function () {
var job = jobs.shift(),
worker = new Worker(job.url);
workers++;
worker.addEventListener(
'message',
function (e) {
if (job.cb.call(job.ctx, e.data, worker)) {
worker.terminate();
worker = null;
workers--;
nextJob();
}
},
false,
);
worker.postMessage(job.msg);
})();
}
}
}
// Simple Asynchronous Cache
@ -49,44 +53,46 @@ function WorkerPool(size) {
// Takes one parameter:
// * key
function AsyncCache(miss, hash) {
var cache = {},
self = this;
var cache = {},
self = this;
hash = hash || function(key) {
return key.toString();
hash =
hash ||
function (key) {
return key.toString();
};
// key: the key to lookup in the cache
// cb : the method to call with the value
// Takes one parameter:
// val: the value in the cache for key
// ctx: context for cb.call
this.get = function(key, cb, ctx) {
var k = hash(key);
if (k in cache) {
cb.call(ctx, cache[k]);
} else {
miss.call(ctx, key, function(val) {
self.set(key, val);
self.get(key, cb, ctx);
});
}
};
// key: the key to lookup in the cache
// cb : the method to call with the value
// Takes one parameter:
// val: the value in the cache for key
// ctx: context for cb.call
this.get = function (key, cb, ctx) {
var k = hash(key);
if (k in cache) {
cb.call(ctx, cache[k]);
} else {
miss.call(ctx, key, function (val) {
self.set(key, val);
self.get(key, cb, ctx);
});
}
};
// sets value for key in cache
this.set = function(key, val) {
cache[hash(key)] = val;
};
// sets value for key in cache
this.set = function (key, val) {
cache[hash(key)] = val;
};
}
function hashObj(o) {
var hash = [];
for (var i in o) {
if (o.hasOwnProperty(i)) {
hash.push(o[i].toString());
}
var hash = [];
for (var i in o) {
if (o.hasOwnProperty(i)) {
hash.push(o[i].toString());
}
return hash.join('_');
}
return hash.join('_');
}
/* cfg takes:
@ -99,27 +105,26 @@ function hashObj(o) {
* ctx: context from which to run all functions
*/
function chunkfor(cfg) {
var position = cfg.start;
var position = cfg.start;
function nextchunk() {
if (position < cfg.end) {
function nextchunk() {
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;
iterator < position+(cfg.chunk_size*cfg.step) && iterator < cfg.end;
iterator += cfg.step) {
position += cfg.chunk_size * cfg.step;
cfg.inner.call(cfg.ctx, iterator);
}
position += cfg.chunk_size * cfg.step;
setTimeout( function () {
nextchunk.call(this);
}, 0);
} 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() {
if ($('.primary').attr('data-report') != 'overview') return;
$(function () {
if ($('.primary').attr('data-report') != 'overview') return;
// set up topcharts (defined in topchart.js)
$('.toplist').topChart();
// set up topcharts (defined in topchart.js)
$('.toplist').topChart();
$(window).on("changeview", function(e, view) {
$('.two-up').addClass('loading');
$(window).on('changeview', function (e, view) {
$('.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({
range: ssView.range,
group: ssView.group,
})
}),
);
});
})();
@ -107,7 +107,7 @@
var url =
baseURL +
[metric, 'day', range.start.pretty(''), range.end.pretty('')].join(
'-'
'-',
);
$('#export_data_csv').attr('href', url + '.csv');

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

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

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

@ -1,141 +1,158 @@
(function($) {
// "use strict";
var baseConfig = {
chart: {
backgroundColor: null
},
title: {
text: null
},
plotArea: {
shadow: null,
borderWidth: null
},
tooltip: {
enabled: false
},
plotOptions: {
pie: {
allowPointSelect: false,
dataLabels: {
enabled: false,
color: '#333'
},
animation: false,
size:190
}
},
credits: {enabled:false},
legend: {
enabled:false
},
series: [{
type: 'pie'
}]
};
(function ($) {
// "use strict";
var baseConfig = {
chart: {
backgroundColor: null,
},
title: {
text: null,
},
plotArea: {
shadow: null,
borderWidth: null,
},
tooltip: {
enabled: false,
},
plotOptions: {
pie: {
allowPointSelect: false,
dataLabels: {
enabled: false,
color: '#333',
},
animation: false,
size: 190,
},
},
credits: { enabled: false },
legend: {
enabled: false,
},
series: [
{
type: 'pie',
},
],
};
$.fn.topChart = function(cfg) {
$(this).each(function() {
var $self = $(this),
$win = $(window),
$chart = $self.find('.piechart'),
hChart,
$table = $self.find('table'),
metric = $table.attr('data-metric'),
view = {
'metric': metric,
'group' : 'all'
};
$.fn.topChart = function (cfg) {
$(this).each(function () {
var $self = $(this),
$win = $(window),
$chart = $self.find('.piechart'),
hChart,
$table = $self.find('table'),
metric = $table.attr('data-metric'),
view = {
metric: metric,
group: 'all',
};
// reload the data when the view's range is modified.
$win.on('changeview', function(e, newView) {
// we only want to respond to changes in range.
if (!newView.range) return;
$self.addClass('loading');
$self.removeClass('nodata');
_.extend(view, {'range' : normalizeRange(newView.range)});
$.when(z.StatsManager.getDataRange(view))
.then(function(data) {
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');
}
// reload the data when the view's range is modified.
$win.on('changeview', function (e, newView) {
// we only want to respond to changes in range.
if (!newView.range) return;
$self.addClass('loading');
$self.removeClass('nodata');
_.extend(view, { range: normalizeRange(newView.range) });
$.when(z.StatsManager.getDataRange(view)).then(function (data) {
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');
}
});
};
})(jQuery);

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

@ -1,15 +1,15 @@
$.fn.highlightTerm = function(val) {
// If an item starts with `val`, wrap the matched text with boldness.
val = val.replace(/[^\w\s]/gi, '');
var pat = new RegExp(val, 'gi');
this.each(function() {
var $this = $(this),
txt = $this.html(),
matchedTxt = txt.replace(pat, '<b>$&</b>');
if (txt != matchedTxt) {
$this.html(matchedTxt);
}
});
$.fn.highlightTerm = function (val) {
// If an item starts with `val`, wrap the matched text with boldness.
val = val.replace(/[^\w\s]/gi, '');
var pat = new RegExp(val, 'gi');
this.each(function () {
var $this = $(this),
txt = $this.html(),
matchedTxt = txt.replace(pat, '<b>$&</b>');
if (txt != matchedTxt) {
$this.html(matchedTxt);
}
});
};
/*
@ -22,205 +22,232 @@ $.fn.highlightTerm = function(val) {
* Optional:
* searchType - possible values are 'AMO', 'MKT'
*/
$.fn.searchSuggestions = function($results, processCallback, searchType) {
var $self = this,
$form = $self.closest('form');
$.fn.searchSuggestions = function ($results, processCallback, searchType) {
var $self = this,
$form = $self.closest('form');
if (!$results.length) {
return;
}
if (!$results.length) {
return;
}
var cat = $results.attr('data-cat');
var cat = $results.attr('data-cat');
if (searchType == 'AMO') {
// Some base elements that we don't want to keep creating on the fly.
var msg;
if (cat == 'themes') {
msg = gettext('Search themes for <b>{0}</b>');
} else if (cat == 'apps') {
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);
});
if (searchType == 'AMO') {
// Some base elements that we don't want to keep creating on the fly.
var msg;
if (cat == 'themes') {
msg = gettext('Search themes for <b>{0}</b>');
} else if (cat == 'apps') {
msg = gettext('Search apps for <b>{0}</b>');
} else {
$self.on('keydown', gestureHandler)
.on('keyup paste', _.throttle(inputHandler, 250));
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;
}
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);
// 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');
}
pollVal = setInterval(function () {
gestureHandler($self);
inputHandler($self);
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);
$form.submit(function(e) {
$self.blur();
clearCurrentSuggestions(e);
});
$results.on('dismiss', clearCurrentSuggestions);
$results.find('.sel').click(function(e) {
e.preventDefault();
$form.submit();
});
$(document).keyup(function (e) {
if (fieldFocused(e)) {
return;
}
if (e.which == 83) {
$self.focus();
}
});
$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();
}
});
$results.on('dismiss', clearCurrentSuggestions);
$(document).keyup(function(e) {
if (fieldFocused(e)) {
return;
}
if (e.which == 83) {
$self.focus();
}
});
return this;
return this;
};

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

@ -1,69 +1,82 @@
$(function() {
// When I click on the avatar, append `#id=<id>` to the URL.
$('.user-avatar img').click(_pd(function(e) {
window.location.hash = 'id=' + $('.user-avatar').data('user-id');
e.stopPropagation();
}));
$(function () {
// When I click on the avatar, append `#id=<id>` to the URL.
$('.user-avatar img').click(
_pd(function (e) {
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()) {
$('.more-all, .more-none').click(_pd(function() {
var $this = $(this);
$this.closest('li').find('input:not([disabled])').prop('checked', $this.hasClass('more-all'));
}));
if ($('#user_edit').exists()) {
$('.more-all, .more-none').click(
_pd(function () {
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;
}
// 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'));
var img = $(this).objectUrl();
if (img) {
$a.css('display', 'block');
$avatar.attr('src', img);
}
$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.
$('#admin-login').click(function() {
window.location = $(this).attr('data-url');
$('#admin-login').click(function () {
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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
;(function() {
'use strict';
(function () {
'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 newsletterWrapper = document.getElementById('newsletter_wrap');
var newsletterForm = document.getElementById('newsletter_form');
var newsletterWrapper = document.getElementById('newsletter_wrap');
// handle errors
var errorArray = [];
var newsletterErrors = document.getElementById('newsletter_errors');
function newsletterError(e) {
var errorList = document.createElement('ul');
// handle errors
var errorArray = [];
var newsletterErrors = document.getElementById('newsletter_errors');
function newsletterError(e) {
var errorList = document.createElement('ul');
if(errorArray.length) {
for (var i = 0; i < errorArray.length; i++) {
var item = document.createElement('li');
item.appendChild(document.createTextNode(errorArray[i]));
errorList.appendChild(item);
}
if (errorArray.length) {
for (var i = 0; i < errorArray.length; i++) {
var item = document.createElement('li');
item.appendChild(document.createTextNode(errorArray[i]));
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 {
// 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 {
if(response.errors) {
for (var i = 0; i < response.errors.length; i++) {
errorArray.push(response.errors[i]);
}
}
newsletterError(new Error());
}
} else {
newsletterError(new Error());
if (response.errors) {
for (var i = 0; i < response.errors.length; i++) {
errorArray.push(response.errors[i]);
}
};
}
newsletterError(new Error());
}
} else {
newsletterError(new Error());
}
};
xhr.onerror = function(e) {
newsletterError(e);
};
xhr.onerror = function (e) {
newsletterError(e);
};
var url = newsletterForm.getAttribute('action');
var url = newsletterForm.getAttribute('action');
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
xhr.timeout = 5000;
xhr.ontimeout = newsletterError;
xhr.responseType = 'json';
xhr.send(params);
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.timeout = 5000;
xhr.ontimeout = newsletterError;
xhr.responseType = 'json';
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})
* "1"
*/
var format = (function() {
var re = /\{([^}]+)\}/g;
return function(s, args) {
if (!s) {
throw "Format string is empty!";
}
if (!args) return;
if (!(args instanceof Array || args instanceof Object))
args = Array.prototype.slice.call(arguments, 1);
return s.replace(re, function(_, match){ return args[match]; });
};
var format = (function () {
var re = /\{([^}]+)\}/g;
return function (s, args) {
if (!s) {
throw 'Format string is empty!';
}
if (!args) return;
if (!(args instanceof Array || args instanceof Object))
args = Array.prototype.slice.call(arguments, 1);
return s.replace(re, function (_, match) {
return args[match];
});
};
})();
function template(s) {
if (!s) {
throw "Template string is empty!";
}
return function(args) { return format(s, args); };
if (!s) {
throw 'Template string is empty!';
}
return function (args) {
return format(s, args);
};
}

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

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

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

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

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

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

@ -2,17 +2,23 @@
// to the provided string if a translation is missing.
// TODO: Remove this once we upgrade to a version of Django that contains
// 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) {
var value = django.catalog[singular];
var translation;
if (typeof(value) !== 'undefined') {
if (typeof value !== 'undefined') {
translation = value[django.pluralidx(count)];
if (translation && translation.length > 0) {
return translation;
}
}
return (count == 1) ? singular : plural;
return count == 1 ? singular : plural;
};
window.ngettext = django.ngettext;
}

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

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

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

@ -14,78 +14,85 @@
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null);
(function () {
// CommonJS
SyntaxHighlighter =
SyntaxHighlighter ||
(typeof require !== 'undefined'
? require('shCore').SyntaxHighlighter
: null);
function Brush()
{
function getKeywordsCSS(str)
{
return '\\b([a-z_]|)' + str.replace(/ /g, '(?=:)\\b|\\b([a-z_\\*]|\\*|)') + '(?=:)\\b';
};
function getValuesCSS(str)
{
return '\\b' + str.replace(/ /g, '(?!-)(?!:)\\b|\\b()') + '\:\\b';
};
function Brush() {
function getKeywordsCSS(str) {
return (
'\\b([a-z_]|)' +
str.replace(/ /g, '(?=:)\\b|\\b([a-z_\\*]|\\*|)') +
'(?=:)\\b'
);
}
var keywords = 'ascent azimuth background-attachment background-color background-image background-position ' +
'background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top ' +
'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';
function getValuesCSS(str) {
return '\\b' + str.replace(/ /g, '(?!-)(?!:)\\b|\\b()') + ':\\b';
}
var values = 'above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder '+
'both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed '+
'continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double '+
'embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia '+
'gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic '+
'justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha '+
'lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower '+
'navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset '+
'outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side '+
'rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow '+
'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';
var keywords =
'ascent azimuth background-attachment background-color background-image background-position ' +
'background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top ' +
'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 fonts = '[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif';
this.regexList = [
{ 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
];
var values =
'above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder ' +
'both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed ' +
'continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double ' +
'embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia ' +
'gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic ' +
'justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha ' +
'lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower ' +
'navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset ' +
'outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side ' +
'rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow ' +
'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({
left: /(&lt;|<)\s*style.*?(&gt;|>)/gi,
right: /(&lt;|<)\/\s*style\s*(&gt;|>)/gi
});
};
var fonts =
'[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif';
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['css'];
this.regexList = [
{ 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
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['css'];
SyntaxHighlighter.brushes.CSS = Brush;
// CommonJS
typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
})();

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

@ -14,39 +14,42 @@
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null);
(function () {
// CommonJS
SyntaxHighlighter =
SyntaxHighlighter ||
(typeof require !== 'undefined'
? require('shCore').SyntaxHighlighter
: null);
function Brush()
{
var keywords = 'break case catch class continue ' +
'default delete do else enum export extends false ' +
'for function if implements import in instanceof ' +
'interface let new null package private protected ' +
'static return super switch ' +
'this throw true try typeof var while with yield';
function Brush() {
var keywords =
'break case catch class continue ' +
'default delete do else enum export extends false ' +
'for function if implements import in instanceof ' +
'interface let new null package private protected ' +
'static return super switch ' +
'this throw true try typeof var while with yield';
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);
};
var r = SyntaxHighlighter.regexLib;
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['js', 'jscript', 'javascript', 'json'];
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
];
SyntaxHighlighter.brushes.JScript = Brush;
this.forHtmlScript(r.scriptScriptTags);
}
// CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['js', 'jscript', 'javascript', 'json'];
SyntaxHighlighter.brushes.JScript = Brush;
// CommonJS
typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
})();

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

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

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

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

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

@ -14,58 +14,90 @@
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null);
(function () {
// CommonJS
SyntaxHighlighter =
SyntaxHighlighter ||
(typeof require !== 'undefined'
? require('shCore').SyntaxHighlighter
: null);
function Brush()
{
function process(match, regexInfo)
{
var constructor = SyntaxHighlighter.Match,
code = match[0],
tag = XRegExp.exec(code, XRegExp('(&lt;|<)[\\s\\/\\?!]*(?<name>[:\\w-\\.]+)', 'xg')),
result = []
;
function Brush() {
function process(match, regexInfo) {
var constructor = SyntaxHighlighter.Match,
code = match[0],
tag = XRegExp.exec(
code,
XRegExp('(&lt;|<)[\\s\\/\\?!]*(?<name>[:\\w-\\.]+)', 'xg'),
),
result = [];
if (match.attributes != null) {
var attributes,
pos = 0,
regex = XRegExp(
'(?<name> [\\w:.-]+)' +
'\\s*=\\s*' +
'(?<value> ".*?"|\'.*?\'|\\w+)',
'xg',
);
if (match.attributes != null)
{
var attributes,
pos = 0,
regex = XRegExp('(?<name> [\\w:.-]+)' +
'\\s*=\\s*' +
'(?<value> ".*?"|\'.*?\'|\\w+)',
'xg');
while ((attributes = XRegExp.exec(code, regex, pos)) != null) {
result.push(
new constructor(
attributes.name,
match.index + attributes.index,
'color1',
),
);
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)
{
result.push(new constructor(attributes.name, match.index + attributes.index, 'color1'));
result.push(new constructor(attributes.value, match.index + attributes.index + attributes[0].indexOf(attributes.value), 'string'));
pos = attributes.index + attributes[0].length;
}
}
if (tag != null)
result.push(
new constructor(
tag.name,
match.index + tag[0].indexOf(tag.name),
'keyword',
),
);
if (tag != null)
result.push(
new constructor(tag.name, match.index + tag[0].indexOf(tag.name), 'keyword')
);
return result;
}
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 = [
{ 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 }
];
};
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html', 'plist'];
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html', 'plist'];
SyntaxHighlighter.brushes.Xml = Brush;
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.
*/
var dp = {
SyntaxHighlighter : {}
SyntaxHighlighter: {},
};
dp.SyntaxHighlighter = {
parseParams: function(
input,
showGutter,
showControls,
collapseAll,
firstLine,
showColumns
)
{
function getValue(list, name)
{
var regex = XRegExp('^' + name + '\\[(?<value>\\w+)\\]$', 'gi'),
match = null
;
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;
};
parseParams: function (
input,
showGutter,
showControls,
collapseAll,
firstLine,
showColumns,
) {
function getValue(list, name) {
var regex = XRegExp('^' + name + '\\[(?<value>\\w+)\\]$', 'gi'),
match = null;
for (var i = 0; i < list.length; i++)
if ((match = XRegExp.exec(list[i], regex)) != null) return match.value;
var parts = input.split(':'),
brushName = parts[0],
options = {},
straight = { 'true' : true }
reverse = { 'true' : false },
result = null,
defaults = SyntaxHighlighter.defaults
;
for (var i in parts)
options[parts[i]] = 'true';
return null;
}
showGutter = asString(defaultValue(showGutter, defaults.gutter));
showControls = asString(defaultValue(showControls, defaults.toolbar));
collapseAll = asString(defaultValue(collapseAll, defaults.collapse));
showColumns = asString(defaultValue(showColumns, defaults.ruler));
firstLine = asString(defaultValue(firstLine, defaults['first-line']));
function defaultValue(value, def) {
return value != null ? value : def;
}
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 asString(value) {
return value != null ? value.toString() : 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');
var parts = input.split(':'),
brushName = parts[0],
options = {},
straight = { true: true };
(reverse = { true: false }),
(result = null),
(defaults = SyntaxHighlighter.defaults);
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;
for (var i in parts) options[parts[i]] = 'true';
params = dp.SyntaxHighlighter.parseParams(
params,
showGutter,
showControls,
collapseAll,
firstLine,
showColumns
);
showGutter = asString(defaultValue(showGutter, defaults.gutter));
showControls = asString(defaultValue(showControls, defaults.toolbar));
collapseAll = asString(defaultValue(collapseAll, defaults.collapse));
showColumns = asString(defaultValue(showColumns, defaults.ruler));
firstLine = asString(defaultValue(firstLine, defaults['first-line']));
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() {
function text(node, trim) {
var cn = node.childNodes;
var t='';
if (cn.length) {
for (var i=0; i<cn.length; i++) {
t += text(cn[i]);
}
var truncate = (function () {
function text(node, trim) {
var cn = node.childNodes;
var t = '';
if (cn.length) {
for (var i = 0; i < cn.length; 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 {
t = _.escape(node.textContent);
cutoff += chunk;
}
if (trim) {
return t.replace(/^\s+|\s+$/g, "");
}
return t;
oc = chunk;
chunk = Math.ceil(chunk / 2);
}
if (success) break;
}
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 {
cutoff += chunk;
}
oc = chunk;
chunk = Math.ceil(chunk/2);
}
if (success) break;
}
if (showTitle && oldtext != text(textEl, trim)) {
textEl.setAttribute("title", oldtext);
}
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
(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) {
// 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
/**
* Initializing the plugin calling the start function
*
* @return boolean false
*/
function _initialize() {
_start(this,jQueryMatchedObj); // This, in this context, refer to object (link) which the user have clicked
return false; // Avoid the browser following the link
function _initialize() {
_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
*
* @param object objClicked The object (link) whick the user have clicked
* @param object jQueryMatchedObj The jQuery object with all elements matched
*/
function _start(objClicked, jQueryMatchedObj) {
// Hime some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
$('embed, object, select').css({ visibility: 'hidden' });
// 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'),
),
);
}
/**
* Start the jQuery lightBox plugin
*
* @param object objClicked The object (link) whick the user have clicked
* @param object jQueryMatchedObj The jQuery object with all elements matched
*/
function _start(objClicked,jQueryMatchedObj) {
// Hime some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
$('embed, object, select').css({ 'visibility' : 'hidden' });
// 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();
}
/**
}
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
*
* The HTML markup will be like that:
@ -121,326 +137,414 @@
</div>
*
*/
function _set_interface() {
// 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>');
// Get page sizes
var arrPageSizes = ___getPageSize();
// Style overlay and show it
$('#jquery-overlay').css({
backgroundColor: settings.overlayBgColor,
opacity: settings.overlayOpacity,
width: arrPageSizes[0],
height: arrPageSizes[1]
}).fadeIn();
// 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]
}).show();
// Assigning click events in elements to close overlay
$('#jquery-overlay,#jquery-lightbox').click(function() {
_finish();
});
// Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects
$('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function() {
_finish();
return false;
});
// If window was resized, calculate the new overlay dimensions
$(window).resize(function() {
// Get page sizes
var arrPageSizes = ___getPageSize();
// 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]
});
});
function _set_interface() {
// 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>',
);
// Get page sizes
var arrPageSizes = ___getPageSize();
// Style overlay and show it
$('#jquery-overlay')
.css({
backgroundColor: settings.overlayBgColor,
opacity: settings.overlayOpacity,
width: arrPageSizes[0],
height: arrPageSizes[1],
})
.fadeIn();
// 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],
})
.show();
// Assigning click events in elements to close overlay
$('#jquery-overlay,#jquery-lightbox').click(function () {
_finish();
});
// Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects
$('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function () {
_finish();
return false;
});
// If window was resized, calculate the new overlay dimensions
$(window).resize(function () {
// Get page sizes
var arrPageSizes = ___getPageSize();
// 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
*
*/
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);
}
}
$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ height: intImageHeight + (settings.containerBorderSize * 2) });
$('#lightbox-container-image-data-box').css({ width: intImageWidth });
};
/**
* Show the prepared image
*
*/
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();
}
$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({
height: intImageHeight + settings.containerBorderSize * 2,
});
$('#lightbox-container-image-data-box').css({ width: intImageWidth });
}
/**
* Show the prepared image
*
*/
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.
$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' });
// 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',
});
// Show the prev button, if not the first image in set
if ( settings.activeImage != 0 ) {
// Show the images button for Next buttons
$('#lightbox-nav-btnPrev').off().hover(function() {
$(this).css({ 'background' : 'url(' + settings.imageBtnPrev + ') left 45% no-repeat' });
},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 prev button, if not the first image in set
if (settings.activeImage != 0) {
// Show the images button for Next buttons
$('#lightbox-nav-btnPrev')
.off()
.hover(
function () {
$(this).css({
background:
'url(' + settings.imageBtnPrev + ') left 45% no-repeat',
});
},
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
if ( settings.activeImage != ( settings.imageArray.length -1 ) ) {
// Show the images button for Next buttons
$('#lightbox-nav-btnNext').off().hover(function() {
$(this).css({ 'background' : 'url(' + settings.imageBtnNext + ') right 45% no-repeat' });
},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;
});
}
// Enable keyboard navigation
_enable_keyboard_navigation();
// Show the next button, if not the last image in set
if (settings.activeImage != settings.imageArray.length - 1) {
// Show the images button for Next buttons
$('#lightbox-nav-btnNext')
.off()
.hover(
function () {
$(this).css({
background:
'url(' + settings.imageBtnNext + ') right 45% no-repeat',
});
},
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;
});
}
// 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
*
*/
function _enable_keyboard_navigation() {
$(document).keydown(function(objEvent) {
_keyboard_action(objEvent);
});
}
// 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();
}
/**
* 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();
}
}
// 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' });
}
/**
}
}
/**
* 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
* getPageSize() by quirksmode.com
*
* @return Array Return an array with page width, height and window width, height
*/
function ___getPageSize() {
var xScroll, yScroll;
if (window.innerHeight && window.scrollMaxY) {
xScroll = window.innerWidth + window.scrollMaxX;
yScroll = window.innerHeight + window.scrollMaxY;
} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
xScroll = document.body.scrollWidth;
yScroll = document.body.scrollHeight;
} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
xScroll = document.body.offsetWidth;
yScroll = document.body.offsetHeight;
}
var windowWidth, windowHeight;
if (self.innerHeight) { // all except Explorer
if(document.documentElement.clientWidth){
windowWidth = document.documentElement.clientWidth;
} else {
windowWidth = self.innerWidth;
}
windowHeight = self.innerHeight;
} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
windowWidth = document.documentElement.clientWidth;
windowHeight = document.documentElement.clientHeight;
} else if (document.body) { // other Explorers
windowWidth = document.body.clientWidth;
windowHeight = document.body.clientHeight;
}
// for small pages with total height less then height of the viewport
if(yScroll < windowHeight){
pageHeight = windowHeight;
} else {
pageHeight = yScroll;
}
// 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;
};
/**
function ___getPageSize() {
var xScroll, yScroll;
if (window.innerHeight && window.scrollMaxY) {
xScroll = window.innerWidth + window.scrollMaxX;
yScroll = window.innerHeight + window.scrollMaxY;
} else if (document.body.scrollHeight > document.body.offsetHeight) {
// all but Explorer Mac
xScroll = document.body.scrollWidth;
yScroll = document.body.scrollHeight;
} else {
// Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
xScroll = document.body.offsetWidth;
yScroll = document.body.offsetHeight;
}
var windowWidth, windowHeight;
if (self.innerHeight) {
// all except Explorer
if (document.documentElement.clientWidth) {
windowWidth = document.documentElement.clientWidth;
} else {
windowWidth = self.innerWidth;
}
windowHeight = self.innerHeight;
} else if (
document.documentElement &&
document.documentElement.clientHeight
) {
// Explorer 6 Strict Mode
windowWidth = document.documentElement.clientWidth;
windowHeight = document.documentElement.clientHeight;
} else if (document.body) {
// other Explorers
windowWidth = document.body.clientWidth;
windowHeight = document.body.clientHeight;
}
// for small pages with total height less then height of the viewport
if (yScroll < windowHeight) {
pageHeight = windowHeight;
} else {
pageHeight = yScroll;
}
// 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
* getPageScroll() by quirksmode.com
*
* @return Array Return an array with x,y page scroll values.
*/
function ___getPageScroll() {
var xScroll, yScroll;
if (self.pageYOffset) {
yScroll = self.pageYOffset;
xScroll = self.pageXOffset;
} else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
yScroll = document.documentElement.scrollTop;
xScroll = document.documentElement.scrollLeft;
} else if (document.body) {// all other Explorers
yScroll = document.body.scrollTop;
xScroll = document.body.scrollLeft;
}
arrayPageScroll = new Array(xScroll,yScroll)
return arrayPageScroll;
};
/**
* Stop the code execution from a escified time in milisecond
*
*/
function ___pause(ms) {
var date = new Date();
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);
};
function ___getPageScroll() {
var xScroll, yScroll;
if (self.pageYOffset) {
yScroll = self.pageYOffset;
xScroll = self.pageXOffset;
} else if (
document.documentElement &&
document.documentElement.scrollTop
) {
// Explorer 6 Strict
yScroll = document.documentElement.scrollTop;
xScroll = document.documentElement.scrollLeft;
} else if (document.body) {
// all other Explorers
yScroll = document.body.scrollTop;
xScroll = document.body.scrollLeft;
}
arrayPageScroll = new Array(xScroll, yScroll);
return arrayPageScroll;
}
/**
* Stop the code execution from a escified time in milisecond
*
*/
function ___pause(ms) {
var date = new Date();
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

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

@ -35,7 +35,7 @@ const showOrHideDefinition = (select) => {
}`;
}
}
}
};
// 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
@ -47,7 +47,7 @@ const showOrHideName = (select) => {
const isNone = selectedScannerIsNone(select);
const isYara = selectedScannerIsYara(select);
name.style.display = (isNone || isYara) ? 'none' : 'block';
name.style.display = isNone || isYara ? 'none' : 'block';
if (isYara) {
const nameInput = document.querySelector('#id_name');
@ -56,7 +56,7 @@ const showOrHideName = (select) => {
nameInput.value = DEFAULT_RULE_NAME;
}
}
}
};
const scannerSelect = document.querySelector('#id_scanner');
const definitionTextarea = document.querySelector('#id_definition');

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

@ -1,21 +1,22 @@
/** Addons Display page */
/* 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/';
$("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 etiquette_box = $("#addons-display-review-etiquette").hide();
$("#short-review").focus(function() { etiquette_box.show("fast"); } );
var etiquette_box = $('#addons-display-review-etiquette').hide();
$('#short-review').focus(function () {
etiquette_box.show('fast');
});
});

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

@ -1,55 +1,62 @@
(function(){
"use strict";
(function () {
'use strict';
// Add sphinx-like links to headings with ids.
$(function(){
var html = '<a class="headerlink" href="#{0}">&para;</a>';
$(':-moz-any(h1,h2,h3,h4,h5,h6)[id]').each(function() {
console.log(format(html, $(this).attr('id')));
$(this).append(format(html, $(this).attr('id')));
});
// Add sphinx-like links to headings with ids.
$(function () {
var html = '<a class="headerlink" href="#{0}">&para;</a>';
$(':-moz-any(h1,h2,h3,h4,h5,h6)[id]').each(function () {
console.log(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() {
$('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.
}));
// Recalculate Hash
$('.recalc').click(
_pd(function () {
var $this = $(this);
$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);
});
// Recalculate Hash
$('.recalc').click(_pd(function() {
var $this = $(this);
$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' });
}),
);
});
$('#id_start, #id_end').datepicker({ dateFormat: 'yy-mm-dd' });
})();

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

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

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

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

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

@ -9,7 +9,7 @@ function isDoNotTrackEnabled() {
if (dnt === '1') {
window.console &&
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;
}
@ -36,7 +36,7 @@ if (isDoNotTrackEnabled() === false) {
document,
'script',
'https://www.google-analytics.com/analytics.js',
'ga'
'ga',
);
ga('create', 'UA-36116321-7', 'auto');

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

@ -1,163 +1,159 @@
/* Browser Utilities
* Based on amo2009/addons.js
**/
**/
function BrowserUtils() {
"use strict";
'use strict';
var userAgentStrings = {
'firefox' : /^Mozilla.*(Firefox|Minefield|Namoroka|Shiretoko|GranParadiso|BonEcho|Iceweasel|Fennec|MozillaDeveloperPreview)\/([^\s]*).*$/,
'seamonkey': /^Mozilla.*(SeaMonkey|Iceape)\/([^\s]*).*$/,
'mobile': /^Mozilla.*(Fennec|Mobile)\/([^\s]*)$/,
'thunderbird': /^Mozilla.*(Thunderbird|Shredder|Lanikai)\/([^\s*]*).*$/
},
osStrings = {
'windows': /Windows/,
'mac': /Mac/,
'linux': /Linux|BSD/,
'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,
var userAgentStrings = {
firefox: /^Mozilla.*(Firefox|Minefield|Namoroka|Shiretoko|GranParadiso|BonEcho|Iceweasel|Fennec|MozillaDeveloperPreview)\/([^\s]*).*$/,
seamonkey: /^Mozilla.*(SeaMonkey|Iceape)\/([^\s]*).*$/,
mobile: /^Mozilla.*(Fennec|Mobile)\/([^\s]*)$/,
thunderbird: /^Mozilla.*(Thunderbird|Shredder|Lanikai)\/([^\s*]*).*$/,
},
osStrings = {
windows: /Windows/,
mac: /Mac/,
linux: /Linux|BSD/,
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,
};
}
var VersionCompare = {
/**
* Mozilla-style version numbers comparison in Javascript
* (JS-translated version of PHP versioncompare component)
* @return -1: a<b, 0: a==b, 1: a>b
*/
compareVersions: function(a,b) {
var al = a.split('.'),
bl = b.split('.'),
ap, bp, r, i;
for (i=0; i<al.length || i<bl.length; i++) {
ap = (i<al.length ? al[i] : null);
bp = (i<bl.length ? bl[i] : null);
r = this.compareVersionParts(ap,bp);
if (r !== 0)
return r;
}
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;
/**
* Mozilla-style version numbers comparison in Javascript
* (JS-translated version of PHP versioncompare component)
* @return -1: a<b, 0: a==b, 1: a>b
*/
compareVersions: function (a, b) {
var al = a.split('.'),
bl = b.split('.'),
ap,
bp,
r,
i;
for (i = 0; i < al.length || i < bl.length; i++) {
ap = i < al.length ? al[i] : null;
bp = i < bl.length ? bl[i] : null;
r = this.compareVersionParts(ap, bp);
if (r !== 0) return r;
}
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(); */
z.button = {};
/* A library of callbacks that may be run after InstallTrigger succeeds.
* ``this`` will be bound to the .install button.
*/
z.button.after = {'contrib': function(xpi_url, status) {
if (status === 0) { //success
/* A library of callbacks that may be run after InstallTrigger succeeds.
* ``this`` will be bound to the .install button.
*/
z.button.after = {
contrib: function (xpi_url, status) {
if (status === 0) {
//success
document.location = $(this).attr('data-developers');
}
}};
}
},
};
var notavail = '<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>',
var notavail =
'<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+)');
// The lowest maxVersion an app has to support to allow default-to-compatible.
var D2C_MAX_VERSIONS = {
// The lowest maxVersion an app has to support to allow default-to-compatible.
var D2C_MAX_VERSIONS = {
firefox: '4.0',
mobile: '11.0',
seamonkey: '2.1',
thunderbird: '5.0'
};
thunderbird: '5.0',
};
/* Called by the jQuery plugin to set up a single button. */
var installButton = function() {
/* Called by the jQuery plugin to set up a single button. */
var installButton = function () {
// Create a bunch of data and helper functions, then drive the buttons
// based on the button type at the end.
var self = this,
$this = $(this),
$button = $this.find('.button');
$this = $(this),
$button = $this.find('.button');
// Unreviewed and self-hosted buttons point to the add-on detail page for
// non-js safety. Flip them to the real xpi url here.
$button.each(function() {
var $this = $(this);
if ($this.hasattr('data-realurl')) {
$this.attr('href', $this.attr('data-realurl'));
}
$button.each(function () {
var $this = $(this);
if ($this.hasattr('data-realurl')) {
$this.attr('href', $this.attr('data-realurl'));
}
/* If we're on the mobile site but it's not a mobile browser, force
* the download url to type:attachment.
*/
if (z.app === 'mobile' && !z.appMatchesUserAgent) {
var href = $this.attr('href');
$this.attr('href', href.replace(download_re, '$1/type:attachment'));
}
/* If we're on the mobile site but it's not a mobile browser, force
* the download url to type:attachment.
*/
if (z.app === 'mobile' && !z.appMatchesUserAgent) {
var href = $this.attr('href');
$this.attr('href', href.replace(download_re, '$1/type:attachment'));
}
});
var addon = $this.attr('data-addon'),
min = $this.attr('data-min'),
max = $this.attr('data-max'),
name = $this.attr('data-name'),
icon = $this.attr('data-icon'),
after = $this.attr('data-after'),
search = $this.hasattr('data-search'),
no_compat_necessary = $this.hasattr('data-no-compat-necessary'),
accept_eula = $this.hasClass('accept'),
compatible = $this.attr('data-is-compatible-by-default') == 'true',
compatible_app = $this.attr('data-is-compatible-app') == 'true',
has_overrides = $this.hasattr('data-compat-overrides'),
versions_url = $this.attr('data-versions'),
// L10n: {0} is an app name like Firefox.
_s = accept_eula ? gettext('Accept and Install') : gettext('Add to {0}'),
addto = format(_s, [z.appName]),
appSupported = z.appMatchesUserAgent && min && max,
$body = $(document.body),
olderBrowser,
newerBrowser;
min = $this.attr('data-min'),
max = $this.attr('data-max'),
name = $this.attr('data-name'),
icon = $this.attr('data-icon'),
after = $this.attr('data-after'),
search = $this.hasattr('data-search'),
no_compat_necessary = $this.hasattr('data-no-compat-necessary'),
accept_eula = $this.hasClass('accept'),
compatible = $this.attr('data-is-compatible-by-default') == 'true',
compatible_app = $this.attr('data-is-compatible-app') == 'true',
has_overrides = $this.hasattr('data-compat-overrides'),
versions_url = $this.attr('data-versions'),
// L10n: {0} is an app name like Firefox.
_s = accept_eula ? gettext('Accept and Install') : gettext('Add to {0}'),
addto = format(_s, [z.appName]),
appSupported = z.appMatchesUserAgent && min && max,
$body = $(document.body),
olderBrowser,
newerBrowser;
// If we have os-specific buttons, check that one of them matches the
// current platform.
var badPlatform = ($button.find('.os').length &&
!$button.hasClass(z.platform));
var badPlatform =
$button.find('.os').length && !$button.hasClass(z.platform);
// min and max only exist if the add-on is compatible with request[APP].
if (appSupported) {
// The user *has* an older/newer browser.
olderBrowser = VersionCompare.compareVersions(z.browserVersion, min) < 0;
newerBrowser = VersionCompare.compareVersions(z.browserVersion, max) > 0;
if (olderBrowser) {
// Make sure we show the "Not available for ..." messaging.
compatible = false;
}
// The user *has* an older/newer browser.
olderBrowser = VersionCompare.compareVersions(z.browserVersion, min) < 0;
newerBrowser = VersionCompare.compareVersions(z.browserVersion, max) > 0;
if (olderBrowser) {
// Make sure we show the "Not available for ..." messaging.
compatible = false;
}
}
// Default to compatible checking.
if (compatible) {
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 {
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;
}
var addWarning = function(msg, type) {
$this.parent().append(format(type || notavail, [msg]));
var addWarning = function (msg, type) {
$this.parent().append(format(type || notavail, [msg]));
};
// Change the button text to "Add to Firefox".
var addToApp = function() {
if (appSupported || (no_compat_necessary && z.appMatchesUserAgent)) {
$button.addClass('add').removeClass('download')
.find('span').text(addto);
}
var addToApp = function () {
if (appSupported || (no_compat_necessary && z.appMatchesUserAgent)) {
$button
.addClass('add')
.removeClass('download')
.find('span')
.text(addto);
}
};
// Calls InstallTrigger.install or AddSearchProvider if we capture a click
// on something with a .installer class.
var clickHijack = function() {
try {
if (!appSupported && !no_compat_necessary || !("InstallTrigger" in window)) return;
} catch (e) {
return;
}
var clickHijack = function () {
try {
if (
(!appSupported && !no_compat_necessary) ||
!('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.addClass('active');
}).on('mouseup blur', function(e) {
$this.removeClass('active');
}).click(function(e) {
// If the click was on a.installer or a child, call the special
// install method. We can't bind this directly because we add
// more .installers dynamically.
var $target = $(e.target),
$installer = '';
if ($target.hasClass('installer')) {
installer = $target;
} else {
installer = $target.parents('.installer').first();
if (_.indexOf($this.find('.installer'), installer[0]) == -1) {
return;
}
$this
.on('mousedown focus', function (e) {
$this.addClass('active');
})
.on('mouseup blur', function (e) {
$this.removeClass('active');
})
.click(function (e) {
// If the click was on a.installer or a child, call the special
// install method. We can't bind this directly because we add
// more .installers dynamically.
var $target = $(e.target),
$installer = '';
if ($target.hasClass('installer')) {
installer = $target;
} else {
installer = $target.parents('.installer').first();
if (_.indexOf($this.find('.installer'), installer[0]) == -1) {
return;
}
e.preventDefault();
}
e.preventDefault();
// map download url => file hash.
var hashes = {};
$this.find('.button[data-hash]').each(function() {
hashes[$(this).attr('href')] = $(this).attr('data-hash');
});
var hash = hashes[installer.attr('href')];
// map download url => file hash.
var hashes = {};
$this.find('.button[data-hash]').each(function () {
hashes[$(this).attr('href')] = $(this).attr('data-hash');
});
var hash = hashes[installer.attr('href')];
var f = _.haskey(z.button.after, after) ? z.button.after[after] : _.identity,
callback = _.bind(f, self),
install = search ? z.installSearch : z.installAddon;
install(name, installer[0].href, icon, hash, callback);
var f = _.haskey(z.button.after, after)
? z.button.after[after]
: _.identity,
callback = _.bind(f, self),
install = search ? z.installSearch : z.installAddon;
install(name, installer[0].href, icon, hash, callback);
});
};
// Gather the available platforms.
var platforms = $button.map(function() {
var name = $(this).find('.os').attr('data-os'),
text = z.appMatchesUserAgent ?
/* L10n: {0} is an platform like Windows or Linux. */
gettext('Install for {0} anyway') : gettext('Download for {0} anyway');
return {
href: $(this).attr('href'),
msg: format(text, [name])
};
var platforms = $button.map(function () {
var name = $(this).find('.os').attr('data-os'),
text = z.appMatchesUserAgent
? /* L10n: {0} is an platform like Windows or Linux. */
gettext('Install for {0} anyway')
: gettext('Download for {0} anyway');
return {
href: $(this).attr('href'),
msg: format(text, [name]),
};
});
var showDownloadAnyway = function($button) {
var $visibleButton = $button.filter(':visible')
var $installShell = $visibleButton.parents('.install-shell');
var $downloadAnyway = $visibleButton.next('.download-anyway');
if ($downloadAnyway.length) {
// We want to be able to add the download anyway link regardless
// 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
// those combinations to work without the download anyway link
// being shown.
// Append a separator to the .more-versions element:
// if it's displayed we need to separate the download anyway link
// from the text shown in that span.
var $moreVersions = $installShell.find('.more-versions');
$moreVersions.append(' | ');
// In any case, add the download anyway link to the parent div.
// It'll show up regardless of whether we are showing the more
// versions link or not.
var $newParent = $installShell.find('.extra .not-available');
$newParent.append($downloadAnyway);
$downloadAnyway.show();
}
}
var showDownloadAnyway = function ($button) {
var $visibleButton = $button.filter(':visible');
var $installShell = $visibleButton.parents('.install-shell');
var $downloadAnyway = $visibleButton.next('.download-anyway');
if ($downloadAnyway.length) {
// We want to be able to add the download anyway link regardless
// 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
// those combinations to work without the download anyway link
// being shown.
// Append a separator to the .more-versions element:
// if it's displayed we need to separate the download anyway link
// from the text shown in that span.
var $moreVersions = $installShell.find('.more-versions');
$moreVersions.append(' | ');
// In any case, add the download anyway link to the parent div.
// It'll show up regardless of whether we are showing the more
// versions link or not.
var $newParent = $installShell.find('.extra .not-available');
$newParent.append($downloadAnyway);
$downloadAnyway.show();
}
};
// Add version and platform warnings. This is one
// big function since we merge the messaging when bad platform and version
// occur simultaneously.
var versionsAndPlatforms = function(options) {
var opts = $.extend({addWarning: true}, options);
warn = opts.addWarning ? addWarning : _.identity;
var versionsAndPlatforms = function (options) {
var opts = $.extend({ addWarning: true }, options);
warn = opts.addWarning ? addWarning : _.identity;
// Do badPlatform prep out here since we need it in all branches.
if (badPlatform) {
warn(gettext('Not available for your platform'));
$button.addClass('concealed');
$button.first().css('display', 'inherit');
$button.closest('.item.addon').addClass('incompatible');
// Do badPlatform prep out here since we need it in all branches.
if (badPlatform) {
warn(gettext('Not available for your platform'));
$button.addClass('concealed');
$button.first().css('display', 'inherit');
$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)) {
// 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);
}
return true;
} else if (!unreviewed && (appSupported || no_compat_necessary)) {
// Good version, good platform.
$button.addClass('installer');
$button.closest('div').attr('data-version-supported', true);
} else if (!appSupported) {
var msg = (min && max ?
gettext('Works with {app} {min} - {max}') :
gettext('Works with {app}'));
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 true;
} else if (!unreviewed && (appSupported || no_compat_necessary)) {
// Good version, good platform.
$button.addClass('installer');
$button.closest('div').attr('data-version-supported', true);
} else if (!appSupported) {
var msg =
min && max
? gettext('Works with {app} {min} - {max}')
: gettext('Works with {app}');
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?
var unreviewed = $this.hasClass('unreviewed'),
contrib = $this.hasClass('contrib'),
eula = $this.hasClass('eula');
contrib = $this.hasClass('contrib'),
eula = $this.hasClass('eula');
// Drive the install button based on its type.
if (eula || contrib) {
versionsAndPlatforms();
versionsAndPlatforms();
} else if (z.appMatchesUserAgent) {
clickHijack();
addToApp();
var opts = no_compat_necessary ? {addWarning: false} : {};
versionsAndPlatforms(opts);
clickHijack();
addToApp();
var opts = no_compat_necessary ? { addWarning: false } : {};
versionsAndPlatforms(opts);
} else if (z.app == 'firefox') {
$button.addClass('CTA');
$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');
$('#site-nonfx').hide();
$button.addClass('CTA');
$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',
);
$('#site-nonfx').hide();
} else if (z.app == 'thunderbird') {
versionsAndPlatforms();
versionsAndPlatforms();
} else {
clickHijack();
addToApp();
versionsAndPlatforms();
clickHijack();
addToApp();
versionsAndPlatforms();
}
};
};
jQuery.fn.installButton = function() {
jQuery.fn.installButton = function () {
return this.each(installButton);
};
};
/* Install an XPI or a JAR (or something like that).
*
* hash and callback are optional. callback is triggered after the
* installation is complete.
*/
z.installAddon = function(name, url, icon, hash, callback) {
/* Install an XPI or a JAR (or something like that).
*
* hash and callback are optional. callback is triggered after the
* installation is complete.
*/
z.installAddon = function (name, url, icon, hash, callback) {
var params = {};
params[name] = {
URL: url,
IconURL: icon,
toString: function() { return url; }
URL: url,
IconURL: icon,
toString: function () {
return url;
},
};
if (hash) {
params[name].Hash = hash;
params[name].Hash = hash;
}
// InstallTrigger is a Gecko API.
InstallTrigger.install(params, callback);
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) {
window.external.AddSearchProvider(url);
callback();
ga('send', 'event', 'AMO Addon / Theme Installs', 'addon', name);
window.external.AddSearchProvider(url);
callback();
ga('send', 'event', 'AMO Addon / Theme Installs', 'addon', name);
} else {
// Alert! Deal with it.
alert(gettext('Sorry, you need a Mozilla-based browser (such as Firefox) to install a search plugin.'));
// Alert! Deal with it.
alert(
gettext(
'Sorry, you need a Mozilla-based browser (such as Firefox) to install a search plugin.',
),
);
}
};
};
})();

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

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

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

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

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

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

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

@ -34,30 +34,35 @@ _.template(`
*/
/* The following is the above commented template, pre-compiled. */
function syntaxhighlighter_template(obj){
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
with(obj||{}){
__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 ';
_.each(lines, function(line) {
__p+='\n <tr class="tr-line">\n <td class="td-line-number">\n <a href="#'+
((__t=( line.id ))==null?'':_.escape(__t))+
'" id="'+
((__t=( line.id ))==null?'':_.escape(__t))+
'"\n class="'+
((__t=( line.class ))==null?'':_.escape(__t))+
' original line line-number"\n data-linenumber="'+
((__t=( line.number ))==null?'':_.escape(__t))+
'"></a>\n </td>\n <td class="'+
((__t=( line.class ))==null?'':_.escape(__t))+
' td-line-code alt'+
((__t=( line.number % 2 + 1))==null?'':_.escape(__t))+
'"><span\n class="original line line-code">'+
((__t=(
line.code
))==null?'':__t)+
'</span></td>\n </tr>\n ';
})
__p+='\n </tbody>\n </table>\n </div>\n';
}
return __p;
function syntaxhighlighter_template(obj) {
var __t,
__p = '',
__j = Array.prototype.join,
print = function () {
__p += __j.call(arguments, '');
};
with (obj || {}) {
__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 ';
_.each(lines, function (line) {
__p +=
'\n <tr class="tr-line">\n <td class="td-line-number">\n <a href="#' +
((__t = line.id) == null ? '' : _.escape(__t)) +
'" id="' +
((__t = line.id) == null ? '' : _.escape(__t)) +
'"\n class="' +
((__t = line.class) == null ? '' : _.escape(__t)) +
' original line line-number"\n data-linenumber="' +
((__t = line.number) == null ? '' : _.escape(__t)) +
'"></a>\n </td>\n <td class="' +
((__t = line.class) == null ? '' : _.escape(__t)) +
' td-line-code alt' +
((__t = (line.number % 2) + 1) == null ? '' : _.escape(__t)) +
'"><span\n class="original line line-code">' +
((__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
// 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;
// Block anything that starts with 'http:', 'https:', '://' or '//'.
if (!/^((https?:)|:?[/]{2})/.test(ajaxSettings.url)) {
// Only send the token to relative URLs i.e. locally.
$meta = $('meta[name=csrf]');
if (!z.anonymous && $meta.length) {
csrf = $meta.attr('content');
} else {
csrf = $("input[name='csrfmiddlewaretoken']").val();
}
if (csrf) {
xhr.setRequestHeader('X-CSRFToken', csrf);
}
// Only send the token to relative URLs i.e. locally.
$meta = $('meta[name=csrf]');
if (!z.anonymous && $meta.length) {
csrf = $meta.attr('content');
} else {
csrf = $("input[name='csrfmiddlewaretoken']").val();
}
if (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.
});
});
function b64toBlob(data) {
var b64str = atob(data);
var counter = b64str.length;
var u8arr = new Uint8Array(counter);
while(counter--){
u8arr[counter] = b64str.charCodeAt(counter);
}
return new Blob([u8arr]);
var b64str = atob(data);
var counter = b64str.length;
var u8arr = new Uint8Array(counter);
while (counter--) {
u8arr[counter] = b64str.charCodeAt(counter);
}
return new Blob([u8arr]);
}

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

@ -1,49 +1,51 @@
$(document).ready(function(){
if (!$(document.body).hasClass('home')) {
return;
$(document).ready(function () {
if (!$(document.body).hasClass('home')) {
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) {
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);
}
};
// 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);
// 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);
}
// Set up our history callback.
$(window).on('popstate', function(ev) {
// We don't pushState here because we'd be stuck in this position.
var e = ev.originalEvent;
if (e.state && e.state.target) {
var a = $('#' + e.state.target + ' a')[0];
update(a, false);
}
});
// Set up our history callback.
$(window).on('popstate', function (ev) {
// We don't pushState here because we'd be stuck in this position.
var e = ev.originalEvent;
if (e.state && e.state.target) {
var a = $('#' + e.state.target + ' a')[0];
update(a, false);
}
});
});

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

@ -1,121 +1,133 @@
/* Global initialization script */
var z = {};
$(document).ready(function(){
// Initialize install buttons.
$('.install').installButton();
$(window).trigger('buttons_loaded');
$(document).ready(function () {
// Initialize install buttons.
$('.install').installButton();
$(window).trigger('buttons_loaded');
// Initialize any tabbed interfaces. See: tabs.js
if ($.fn.tabify) {
$('.tab-wrapper').tabify();
}
// Initialize any tabbed interfaces. See: tabs.js
if ($.fn.tabify) {
$('.tab-wrapper').tabify();
}
// Initialize email links
$('span.emaillink').each(function() {
$(this).find('.i').remove();
var em = $(this).text().split('').reverse().join('');
$(this).prev('a').attr('href', 'mailto:' + em).addClass('email');
});
// Initialize email links
$('span.emaillink').each(function () {
$(this).find('.i').remove();
var em = $(this).text().split('').reverse().join('');
$(this)
.prev('a')
.attr('href', 'mailto:' + em)
.addClass('email');
});
// fake placeholders if we need to.
if (!('placeholder' in document.createElement('input'))) {
$('input[placeholder]').placeholder();
}
// fake placeholders if we need to.
if (!('placeholder' in document.createElement('input'))) {
$('input[placeholder]').placeholder();
}
if (z.readonly) {
$('form[method=post]')
.before(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');
}
if (z.readonly) {
$('form[method=post]')
.before(
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');
e.innerHTML = '<svg></svg>';
return !!(window.SVGSVGElement && e.firstChild instanceof window.SVGSVGElement);
return !!(
window.SVGSVGElement && e.firstChild instanceof window.SVGSVGElement
);
})();
if (!z.inlineSVG) {
$("body").addClass("noInlineSVG");
$('body').addClass('noInlineSVG');
}
/* prevent-default function wrapper */
function _pd(func) {
return function(e) {
e.preventDefault();
func.apply(this, arguments);
};
return function (e) {
e.preventDefault();
func.apply(this, arguments);
};
}
/* 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) {
this.attr('placeholder', new_value);
/* Bail early if we have built-in placeholder support. */
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 ('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');
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(function() {
var $this = $(this),
text = $this.attr('placeholder');
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();
});
})
.blur();
};
jQuery.fn.hasattr = function(name) {
return this.attr(name) !== undefined;
jQuery.fn.hasattr = function (name) {
return this.attr(name) !== undefined;
};
var escape_ = function(s){
if (s === undefined) {
return;
}
return s.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;')
.replace(/'/g, '&#39;').replace(/"/g, '&#34;');
var escape_ = function (s) {
if (s === undefined) {
return;
}
return s
.replace(/&/g, '&amp;')
.replace(/>/g, '&gt;')
.replace(/</g, '&lt;')
.replace(/'/g, '&#39;')
.replace(/"/g, '&#34;');
};
//TODO(potch): kill underscore dead. until then, fake it on mobile.
if (!('_' in window)) _ = {};
/* is ``key`` in obj? */
_.haskey = function(obj, key) {
return typeof obj[key] !== "undefined";
_.haskey = function (obj, key) {
return typeof obj[key] !== 'undefined';
};
/* Detect browser, version, and OS. */
$.extend(z, BrowserUtils());
$(document.body).addClass(z.platform).toggleClass('badbrowser', z.badBrowser);
/* Details for the current application. */
z.app = document.body.getAttribute('data-app');
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'));
if (z.badBrowser) {
$(".get-fx-message").show();
$('.get-fx-message').show();
}

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

@ -1,297 +1,337 @@
// Yes, this is out here for a reason.
// We want to hide the non-default locales as fast as possible.
(function() {
var dl = $('body').attr("data-default-locale");
if (dl) {
$(format(".trans>:not([lang='{0}'])", dl)).hide();
$(format(".trans [lang='{0}']", dl)).show();
}
(function () {
var dl = $('body').attr('data-default-locale');
if (dl) {
$(format(".trans>:not([lang='{0}'])", dl)).hide();
$(format(".trans [lang='{0}']", dl)).show();
}
})();
$(document).ready(function () {
if (!$("#l10n-menu").length) return;
var locales = [],
dl = $('body').attr("data-default-locale"),
currentLocale = dl,
unsavedModalMsg = $('#modal-l10n-unsaved .msg').html(),
unsavedModal = $('#modal-l10n-unsaved').modal(),
rmLocaleModalMsg = $('#modal-l10n-rm .msg').html(),
rmLocaleModal = $('#modal-l10n-rm').modal(),
modalActions = $(".modal-actions", unsavedModal),
translations = {}; //hold the initial values of the fields to check for changes
if (!$('#l10n-menu').length) return;
var locales = [],
dl = $('body').attr('data-default-locale'),
currentLocale = dl,
unsavedModalMsg = $('#modal-l10n-unsaved .msg').html(),
unsavedModal = $('#modal-l10n-unsaved').modal(),
rmLocaleModalMsg = $('#modal-l10n-rm .msg').html(),
rmLocaleModal = $('#modal-l10n-rm').modal(),
modalActions = $('.modal-actions', unsavedModal),
translations = {}; //hold the initial values of the fields to check for changes
$(".primary").on("change keyup paste blur", ".trans input, .trans textarea", checkTranslation);
$("form").submit(function () {
$(this).find(".trans .cloned").remove();
$('.primary').on(
'change keyup paste blur',
'.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
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 showExistingLocales() {
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();
}
});
}
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;
}
function showExistingLocales() {
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 (!(transKey in translations)) {
translations[transKey] = $input.val();
}
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)) {
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");
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("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);
$("#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');
if (localePopup) {
localePopup.hideMe();
}
$(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();
$tgt = $(this);
var new_locale = $tgt.attr("data-lang") || $tgt.attr("href").substring(1);
var unsaved = $("form .trans .unsaved");
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;
},
});
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();
});
function updateLocale(lang) {
lang = lang || currentLocale;
if (currentLocale != lang) {
currentLocale = lang;
}
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 {
updateLocale(new_locale);
}
if(localePopup) {
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;
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);
}
});
function updateLocale(lang) {
lang = lang || currentLocale;
if (currentLocale != lang) {
currentLocale = lang;
}
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});
}
$(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) {
var seen_locales = {};
$(".trans [lang]").each(function () {
seen_locales[$(this).attr('lang')] = true;
});
locales = _.keys(seen_locales);
}
function discoverLocales(locale) {
var seen_locales = {};
$('.trans [lang]').each(function () {
seen_locales[$(this).attr('lang')] = true;
});
locales = _.keys(seen_locales);
}
z.refreshL10n = function(lang) {
updateLocale(lang);
};
updateLocale();
z.refreshL10n = function (lang) {
updateLocale(lang);
};
updateLocale();
});
function annotateLocalizedErrors($el) {
$el.find(".errorlist li[data-lang]:not(.l10n)").each(function() {
var err = $(this),
t = err.text(),
l = $(format("#locale-popup [href='#{0}']", [err.attr('data-lang')])).first().text();
err.text(format("{0}: ",[l])+t).addClass("l10n");
});
$el.find('.errorlist li[data-lang]:not(.l10n)').each(function () {
var err = $(this),
t = err.text(),
l = $(format("#locale-popup [href='#{0}']", [err.attr('data-lang')]))
.first()
.text();
err.text(format('{0}: ', [l]) + t).addClass('l10n');
});
}
function loc(s) {
// A noop function for strings that are not ready to be localized.
return s;
// A noop function for strings that are not ready to be localized.
return s;
}

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

@ -1,111 +1,114 @@
$(document).ready(function() {
var report = $('.review-reason').html();
$(document).ready(function () {
var report = $('.review-reason').html();
$(".review-reason").popup(".flag-review", {
delegate: $(document.body),
width: 'inherit',
callback: function(obj) {
var ct = $(obj.click_target),
$popup = this;
$('.review-reason').popup('.flag-review', {
delegate: $(document.body),
width: 'inherit',
callback: function (obj) {
var ct = $(obj.click_target),
$popup = this;
function addFlag(flag, note) {
$.ajax({type: 'POST',
url: ct.attr("href"),
data: {flag: flag, note: note},
success: function() {
$popup.removeClass("other")
.hideMe();
ct.replaceWith(gettext('Flagged for review'));
},
error: function(){ },
dataType: 'json'
});
};
function addFlag(flag, note) {
$.ajax({
type: 'POST',
url: ct.attr('href'),
data: { flag: flag, note: note },
success: function () {
$popup.removeClass('other').hideMe();
ct.replaceWith(gettext('Flagged for review'));
},
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.html(report);
return { pointTo: ct };
}
});
$('.primary').on('click', '.review-edit', function(e) {
$popup.on('click', 'li a', function (e) {
e.preventDefault();
var $form = $("#review-edit-form"),
$review = $(this).parents(".review"),
rating = $review.attr("data-rating"),
edit_url = $("a.permalink", $review).attr("href") + "edit";
$cancel = $("#review-edit-cancel");
$review.attr("action", edit_url);
$form.detach().insertAfter($review);
$("#id_title").val($review.children("h5").text());
$(".ratingwidget input:radio[value=" + rating + "]", $form).click();
$("#id_body").val($review.children("p.review-body").text());
$review.hide();
$form.show();
function done_edit() {
$form.off().hide();
$review.show();
$cancel.off();
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));
}
});
$cancel.click(function(e) {
e.preventDefault();
done_edit();
});
$popup.html(report);
return { pointTo: ct };
},
});
$form.submit(function (e) {
e.preventDefault();
$.ajax({type: 'POST',
url: edit_url,
data: $form.serialize(),
success: function(response, status) {
$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;
});
$('.primary').on('click', '.review-edit', function (e) {
e.preventDefault();
var $form = $('#review-edit-form'),
$review = $(this).parents('.review'),
rating = $review.attr('data-rating'),
edit_url = $('a.permalink', $review).attr('href') + 'edit';
$cancel = $('#review-edit-cancel');
$review.attr('action', edit_url);
$form.detach().insertAfter($review);
$('#id_title').val($review.children('h5').text());
$('.ratingwidget input:radio[value=' + rating + ']', $form).click();
$('#id_body').val($review.children('p.review-body').text());
$review.hide();
$form.show();
function done_edit() {
$form.off().hide();
$review.show();
$cancel.off();
}
$cancel.click(function (e) {
e.preventDefault();
done_edit();
});
$('.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');
$form.submit(function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: edit_url,
data: $form.serialize(),
success: function (response, status) {
$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() {
var $wizard = $(this);
var preLoadBlob = null;
var headerImageError = false;
function getFile() {
file_selector = $wizard.find('#header-img')[0];
file = file_selector.files[0];
if (file && $wizard.find('#header-img').attr('accept').split(',').indexOf(file.type) == -1)
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() !== "";
}
function getFile() {
file_selector = $wizard.find('#header-img')[0];
file = file_selector.files[0];
if (
file &&
$wizard
.find('#header-img')
.attr('accept')
.split(',')
.indexOf(file.type) == -1
)
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() !== ''
);
}
}
});

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

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

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

@ -7,131 +7,131 @@
* .tabify() plugin so it can be accessed later.
*/
var Tabs = function(el) {
this.root = $(el);
this.init();
var Tabs = function (el) {
this.root = $(el);
this.init();
};
Tabs.prototype = {
init: function() {
this.root.addClass('tab-wrapper');
this.tabMap = {};
this.panelMap = {};
this.reset();
init: function () {
this.root.addClass('tab-wrapper');
this.tabMap = {};
this.panelMap = {};
this.reset();
this.select();
this.select();
/* Bind hashchange, trigger event to check for existing hash. */
var self = this;
$(document).on('hashchange', function(e) {
self.hashChange(e);
}).trigger('hashchange');
},
/* Bind hashchange, trigger event to check for existing hash. */
var self = this;
$(document)
.on('hashchange', function (e) {
self.hashChange(e);
})
.trigger('hashchange');
},
/* Find and prepare all the tabs and panels. Can be called multiple times,
* e.g. to update tabs after insertion/deletion.
*/
reset: function(o) {
this.findTabs();
this.findPanels();
this.styleTabs(this.tabs);
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);
});
/* Find and prepare all the tabs and panels. Can be called multiple times,
* e.g. to update tabs after insertion/deletion.
*/
reset: function (o) {
this.findTabs();
this.findPanels();
this.styleTabs(this.tabs);
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;
};
/* Change location.hash without scrolling. */
var safeHashChange = function(hash) {
var el = $(hash);
el.attr('id', '');
location.hash = hash;
el.attr('id', hash.slice(1));
var safeHashChange = function (hash) {
var el = $(hash);
el.attr('id', '');
location.hash = hash;
el.attr('id', hash.slice(1));
};

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

@ -1,22 +1,31 @@
$(document).ready(function(){
if (!z.appMatchesUserAgent) {
return;
$(document).ready(function () {
if (!z.appMatchesUserAgent) {
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() {
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);
$('.browse-thumbs .thumbs li').each(themeCompat);
});

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

@ -1,470 +1,529 @@
(function($) {
/* jQuery.ScrollTo by Ariel Flesler */
$.fn.scrollTo = function(opts) {
if (!this.length) return this;
opts = $.extend({
duration: 250,
marginTop: 0,
complete: undefined
}, opts || { });
var top = this.offset().top - opts.marginTop;
$('html, body').animate({ 'scrollTop': top }, opts.duration, undefined, opts.complete);
return this;
};
(function ($) {
/* jQuery.ScrollTo by Ariel Flesler */
$.fn.scrollTo = function (opts) {
if (!this.length) return this;
opts = $.extend(
{
duration: 250,
marginTop: 0,
complete: undefined,
},
opts || {},
);
var top = this.offset().top - opts.marginTop;
$('html, body').animate(
{ scrollTop: top },
opts.duration,
undefined,
opts.complete,
);
return this;
};
})(jQuery);
(function ($) {
var win = $(window);
var doc = $(document);
(function($) {
var win = $(window);
var doc = $(document);
$.fn.themeQueue = function () {
return this.each(function () {
var queue = this;
var currentTheme = 0;
var cacheQueueHeight;
var $queueContext = $('.queue-context');
var actionConstants = $queueContext.data('actions');
$.fn.themeQueue = function() {
return this.each(function() {
var queue = this;
var currentTheme = 0;
var cacheQueueHeight;
var themesList = $('div.theme', queue);
var themes = themesList
.map(function () {
return {
element: this,
top: 0,
};
})
.get();
var $queueContext = $('.queue-context');
var actionConstants = $queueContext.data('actions');
function nthTheme(i) {
return themesList[i];
}
var themesList = $('div.theme', queue);
var themes = themesList.map(function() {
return {
element: this,
top: 0
};
}).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();
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),
);
function updateMetrics() {
var queueHeight = $(queue).height();
if (queueHeight === cacheQueueHeight) return;
cacheQueueHeight = queueHeight;
doc.keyup(function (e) {
if (!$(queue).hasClass('shortcuts')) return;
$.each(themes, function(i, obj) {
var elem = $(obj.element);
obj.top = elem.offset().top + elem.outerHeight()/2;
});
}
// Ignore key-bindings when textarea focused.
if (fieldFocused(e) && e.which != z.keys.ENTER) return;
function getThemeParent(elem) {
// Given an element (like an approve button),
// return the theme for which it is related to.
return $(elem).closest('.theme').data('id');
}
// For using Enter to submit textareas.
if (e.which == z.keys.ENTER && z.keys.ENTER in keymap) {
keymap[z.keys.ENTER]();
}
function goToTheme(i, delay, duration) {
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();
}
var key = String.fromCharCode(e.which).toLowerCase();
function switchTheme(i) {
if (!themes[currentTheme]) {
return;
}
if (!(key in keymap)) {
return;
}
$(themes[currentTheme].element).removeClass('active');
$(themes[i].element).addClass('active');
vertAlignSidebar(win, $('.theme.active'));
currentTheme = i;
$('.rq-dropdown').hide();
}
var action = keymap[key];
if (action && !e.ctrlKey && !e.altKey && !e.metaKey) {
themeActions[action[0]](currentTheme, action[1]);
}
});
function findCurrentTheme() {
// Uses location of the window within the page to determine
// which theme we're currently looking at.
// Pressing Enter in text field doesn't add carriage return.
$('textarea').keypress(function (e) {
if (e.keyCode == z.keys.ENTER) {
e.preventDefault();
}
});
// $(window).scroll() fires too early.
if (!themes[currentTheme]) {
return 0;
}
$('.theme', queue).removeClass('active');
updateMetrics();
switchTheme(findCurrentTheme());
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;
}
}
}
}
function updateMetrics() {
var queueHeight = $(queue).height();
if (queueHeight === cacheQueueHeight) return;
cacheQueueHeight = queueHeight;
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));
}));
$.each(themes, function (i, obj) {
var elem = $(obj.element);
obj.top = elem.offset().top + elem.outerHeight() / 2;
});
};
}
$.fn.themeQueueOptions = function(queueSelector) {
return this.each(function() {
var self = this;
function getThemeParent(elem) {
// Given an element (like an approve button),
// return the theme for which it is related to.
return $(elem).closest('.theme').data('id');
}
$('input', self).click(onChange);
$('select', self).change(onChange);
onChange();
function goToTheme(i, delay, duration) {
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 onChange(e) {
var category = $('#rq-category', self).val();
var advance = $('#rq-advance:checked', self).val();
var shortcuts = $('#rq-shortcuts:checked', self).val();
function switchTheme(i) {
if (!themes[currentTheme]) {
return;
}
$(queueSelector)
.toggleClass('advance', !!advance)
.toggleClass('shortcuts', !!shortcuts);
$(themes[currentTheme].element).removeClass('active');
$(themes[i].element).addClass('active');
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);
function vertAlignSidebar(win) {
var activeThemeTop = ($('.theme.active').offset().top -
win.scrollTop());
$('.sidebar .align.fixed').css('top', activeThemeTop + 'px');
var activeThemeTop = $('.theme.active').offset().top - win.scrollTop();
$('.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() {
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();
}));
// 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');
}
// Align sidebar with active theme.
if ($('.theme.active').length) {
var win = $(window);
win.scroll(
_.throttle(function () {
vertAlignSidebar(win);
}, 100),
);
vertAlignSidebar(win);
}
if ($('.theme-search').length) {
initSearch();
// 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) {
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'),
$appQueue = $('.search-toggle'),
$search = $('.queue-search'),
$searchIsland = $('#search-island');
var $clear = $('.clear-queue-search'),
$appQueue = $('.search-toggle'),
$search = $('.queue-search'),
$searchIsland = $('#search-island');
if ($search.length) {
var apiUrl = $search.data('api-url');
var review_url = $search.data('review-url');
var statuses = $searchIsland.data('statuses');
if ($search.length) {
var apiUrl = $search.data('api-url');
var review_url = $search.data('review-url');
var statuses = $searchIsland.data('statuses');
$('form', $search).submit(_pd(function() {
var $form = $(this);
$.get(apiUrl, $form.serialize()).done(function(data) {
// Hide app queue.
$appQueue.hide();
$clear.show();
// Show results.
if (data.meta.total_count === 0) {
$searchIsland.html(no_results).show().removeClass('hidden');
} else {
var results = [];
$.each(data.objects, function(i, item) {
item = buildThemeResultRow(item, review_url,
statuses);
results.push(search_result_row_template(item));
});
$searchIsland.html(search_results_template({rows: results.join('')}));
$searchIsland.removeClass('hidden').show();
}
$('form', $search).submit(
_pd(function () {
var $form = $(this);
$.get(apiUrl, $form.serialize()).done(function (data) {
// Hide app queue.
$appQueue.hide();
$clear.show();
// Show results.
if (data.meta.total_count === 0) {
$searchIsland.html(no_results).show().removeClass('hidden');
} else {
var results = [];
$.each(data.objects, function (i, item) {
item = buildThemeResultRow(item, review_url, statuses);
results.push(search_result_row_template(item));
});
}));
}
$searchIsland.html(
search_results_template({ rows: results.join('') }),
);
$searchIsland.removeClass('hidden').show();
}
});
}),
);
}
}
function buildThemeResultRow(theme, review_url, statuses) {
// Add some extra pretty attrs for the template.
theme.name = theme.name[0];
// Add some extra pretty attrs for the template.
theme.name = theme.name[0];
// Rather resolve URLs in backend, infer from slug.
theme.review_url = review_url.replace(
'__slug__', theme.slug);
theme.status = statuses[theme.status];
return theme;
// Rather resolve URLs in backend, infer from slug.
theme.review_url = review_url.replace('__slug__', theme.slug);
theme.status = statuses[theme.status];
return theme;
}

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

@ -24,22 +24,27 @@ _.template(`
/* The following is the above commented template, pre-compiled. */
function search_results_template(obj){
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
with(obj||{}){
__p+='\n <table id="search-queue" class="data-grid items">\n <thead>\n <tr class="listing-header">\n <th>'+
((__t=( gettext('Theme') ))==null?'':_.escape(__t))+
'</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';
function search_results_template(obj) {
var __t,
__p = '',
__j = Array.prototype.join,
print = function () {
__p += __j.call(arguments, '');
};
with (obj || {}) {
__p +=
'\n <table id="search-queue" class="data-grid items">\n <thead>\n <tr class="listing-header">\n <th>' +
((__t = gettext('Theme')) == null ? '' : _.escape(__t)) +
'</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(`
@ -59,24 +64,32 @@ _.template(`
*/
/* The following is the above commented template, pre-compiled. */
function search_result_row_template(obj){
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
with(obj||{}){
__p+='\n <tr class="addon-row">\n <td class="app-name">\n <span class="addon-id">'+
((__t=( id ))==null?'':__t)+
'</span>\n <a href="'+
((__t=( review_url ))==null?'':__t)+
'">'+
((__t=( name ))==null?'':_.escape(__t))+
'</a>\n </td>\n <td>\n ';
if (typeof reviewer !== "undefined") {
__p+='\n '+
((__t=( reviewer ))==null?'':_.escape(__t))+
'\n ';
}
__p+='\n </td>\n <td>'+
((__t=( status ))==null?'':__t)+
'</td>\n </tr>';
}
return __p;
function search_result_row_template(obj) {
var __t,
__p = '',
__j = Array.prototype.join,
print = function () {
__p += __j.call(arguments, '');
};
with (obj || {}) {
__p +=
'\n <tr class="addon-row">\n <td class="app-name">\n <span class="addon-id">' +
((__t = id) == null ? '' : __t) +
'</span>\n <a href="' +
((__t = review_url) == null ? '' : __t) +
'">' +
((__t = name) == null ? '' : _.escape(__t)) +
'</a>\n </td>\n <td>\n ';
if (typeof reviewer !== 'undefined') {
__p +=
'\n ' +
((__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) {
this.each(function() {
truncate(this, opts);
});
return this;
$.fn.truncate = function (opts) {
this.each(function () {
truncate(this, opts);
});
return this;
};
$.fn.untruncate = function() {
this.each(function() {
var $el = $(this),
oTxt = $el.attr("oldtext");
if (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;
$.fn.untruncate = function () {
this.each(function () {
var $el = $(this),
oTxt = $el.attr('oldtext');
if (oTxt) {
$el.text(oTxt);
}
return this.each(function() {
var $this = $(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'});
}
}
});
});
return this;
};
$.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 -= .5;
$this.css('font-size', fs);
height = $this.height();
}
});
$.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),
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) {
// 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.
// 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";
$(document).trigger('unicode_loaded');
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';
$(document).trigger('unicode_loaded');

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

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

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

@ -1,477 +1,528 @@
$(document).ready(function() {
if ($('.addon-validator-suite').length) {
initValidator();
}
$(document).ready(function () {
if ($('.addon-validator-suite').length) {
initValidator();
}
});
function initValidator($doc) {
$doc = $doc || $(document);
$doc = $doc || $(document);
function inherit(OtherClass, constructor) {
var NewClass = function() {
OtherClass.apply(this, arguments);
if (typeof constructor !== 'undefined') {
constructor.apply(this, arguments);
}
}
$.extend(NewClass.prototype, OtherClass.prototype);
return NewClass;
function inherit(OtherClass, constructor) {
var NewClass = function () {
OtherClass.apply(this, arguments);
if (typeof constructor !== 'undefined') {
constructor.apply(this, arguments);
}
};
$.extend(NewClass.prototype, OtherClass.prototype);
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() {
return null;
if (typeof this.msgSet[msg.uid] !== 'undefined') {
return;
}
this.msgSet[msg.uid] = true;
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();
}
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');
ResultsTier.prototype.clear = function() {
this.$tierResults.empty();
};
tier.tallyMsgType(effectiveType);
msgDiv.attr('id', 'v-msg-' + msg.uid);
msgDiv.addClass('msg-' + effectiveType);
ResultsTier.prototype.tallyMsgType = function(type_) {
this.counts[type_] += 1;
};
// The "message" and "description" properties are escaped and linkified
// before we receive them.
$('h5', msgDiv).html(msg.message); // Sanitized HTML value.
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;
}
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'
});
// 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);
});
// Validate when the page loads.
$('#addon-validator-suite').trigger('validate');
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.
$('#addon-validator-suite').trigger('validate');
}

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

@ -72,10 +72,10 @@ describe(__filename, () => {
stats_stats(global.$);
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(
`${defaultBaseUrl}${report}-day-20191007-20191013.json`
`${defaultBaseUrl}${report}-day-20191007-20191013.json`,
);
});
@ -86,10 +86,10 @@ describe(__filename, () => {
stats_stats(global.$);
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(
`${defaultBaseUrl}${report}-day-20190914-20191013.json`
`${defaultBaseUrl}${report}-day-20190914-20191013.json`,
);
});
@ -116,10 +116,10 @@ describe(__filename, () => {
stats_stats(global.$, fakeSessionStorage);
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(
`${defaultBaseUrl}${report}-day-20191115-20191125.json`
`${defaultBaseUrl}${report}-day-20191115-20191125.json`,
);
});
});

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

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