Introduce Prettier (#15131)
This commit is contained in:
Родитель
f93cc66f3a
Коммит
c2e92e7482
|
@ -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
|
|
@ -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…');
|
||||
},
|
||||
"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…');
|
||||
},
|
||||
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">«</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">»</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">«</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">»</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,'&')
|
||||
.replace(/</g,'<')
|
||||
.replace(/>/g,'>')
|
||||
.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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.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…'),
|
||||
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…'),
|
||||
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]);
|
||||
},
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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 + "'> </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 +
|
||||
"'> </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.
|
||||
*/
|
||||
|
|
|
@ -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: /(<|<)\s*style.*?(>|>)/gi,
|
||||
right: /(<|<)\/\s*style\s*(>|>)/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: /(<|<)\s*style.*?(>|>)/gi,
|
||||
right: /(<|<)\/\s*style\s*(>|>)/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 : /(<|<)%[@!=]?/g,
|
||||
right : /%(>|>)/g
|
||||
});
|
||||
};
|
||||
this.forHtmlScript({
|
||||
left: /(<|<)%[@!=]?/g,
|
||||
right: /%(>|>)/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;
|
||||
})();
|
||||
|
|
|
@ -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('(<|<)[\\s\\/\\?!]*(?<name>[:\\w-\\.]+)', 'xg')),
|
||||
result = []
|
||||
;
|
||||
function Brush() {
|
||||
function process(match, regexInfo) {
|
||||
var constructor = SyntaxHighlighter.Match,
|
||||
code = match[0],
|
||||
tag = XRegExp.exec(
|
||||
code,
|
||||
XRegExp('(<|<)[\\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(
|
||||
'(\\<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\>|>)',
|
||||
'gm',
|
||||
),
|
||||
css: 'color2',
|
||||
}, // <![ ... [ ... ]]>
|
||||
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
|
||||
{
|
||||
regex: XRegExp(
|
||||
'(<|<)[\\s\\/\\?!]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(>|>)',
|
||||
'sg',
|
||||
),
|
||||
func: process,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
this.regexList = [
|
||||
{ regex: XRegExp('(\\<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\>|>)', 'gm'), css: 'color2' }, // <![ ... [ ... ]]>
|
||||
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
|
||||
{ regex: XRegExp('(<|<)[\\s\\/\\?!]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(>|>)', '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;
|
||||
})();
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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 || '…';
|
||||
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 || "…";
|
||||
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}">¶</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}">¶</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…');
|
||||
$.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…');
|
||||
$.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, '&').replace(/>/g, '>').replace(/</g, '<')
|
||||
.replace(/'/g, ''').replace(/"/g, '"');
|
||||
var escape_ = function (s) {
|
||||
if (s === undefined) {
|
||||
return;
|
||||
}
|
||||
return s
|
||||
.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/"/g, '"');
|
||||
};
|
||||
|
||||
//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(' ');
|
||||
}
|
||||
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(' ');
|
||||
}
|
||||
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`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
2
tox.ini
2
tox.ini
|
@ -74,7 +74,7 @@ commands =
|
|||
[testenv:codestyle]
|
||||
recreate = True
|
||||
commands =
|
||||
make setup-codestyle
|
||||
make setup-codestyle install_node_dependencies
|
||||
make lint-codestyle
|
||||
|
||||
[testenv:docs]
|
||||
|
|
Загрузка…
Ссылка в новой задаче