diff --git a/.gitignore b/.gitignore
index d4c2da01..c23b513e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,8 +11,7 @@
# generated files
/static/js/profile_all.js
-/static/js/locale.js
-/static/js/Markup.js
+/static/js/global.js
/static/widgets/power.js
/static/widgets/power/demo.html
/static/widgets/searchbox.js
diff --git a/endpoints/class/class.php b/endpoints/class/class.php
index 70922cc8..50ca4666 100644
--- a/endpoints/class/class.php
+++ b/endpoints/class/class.php
@@ -19,8 +19,6 @@ class ClassBaseResponse extends TemplateResponse implements ICache
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 12];
- protected array $scripts = [[SC_JS_FILE, 'js/swfobject.js']];
-
public int $type = Type::CHR_CLASS;
public int $typeId = 0;
public ?string $expansion = null;
diff --git a/endpoints/compare/compare.php b/endpoints/compare/compare.php
index f2cffbe0..5268795d 100644
--- a/endpoints/compare/compare.php
+++ b/endpoints/compare/compare.php
@@ -20,7 +20,6 @@ class CompareBaseResponse extends TemplateResponse
[SC_JS_FILE, 'js/Draggable.js'],
[SC_JS_FILE, 'js/filters.js'],
[SC_JS_FILE, 'js/Summary.js'],
- [SC_JS_FILE, 'js/swfobject.js'],
[SC_CSS_FILE, 'css/Summary.css']
);
protected array $expectedGET = array(
diff --git a/endpoints/icon/get-id-from-name.php b/endpoints/icon/get-id-from-name.php
new file mode 100644
index 00000000..178602cc
--- /dev/null
+++ b/endpoints/icon/get-id-from-name.php
@@ -0,0 +1,29 @@
+ ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[\w_-]+$/']]
+ );
+
+ protected function generate() : void
+ {
+ if (!$this->assertGET('name'))
+ {
+ $this->result = 'null';
+ return;
+ }
+
+ $this->result = 0;
+ if ($id = DB::Aowow()->selectCell('SELECT `id` FROM ?_icons WHERE `name` = ?', $this->_get['name']))
+ $this->result = $id;
+ }
+}
+
+?>
diff --git a/endpoints/item/item.php b/endpoints/item/item.php
index 5d81b9b2..f476b5b9 100644
--- a/endpoints/item/item.php
+++ b/endpoints/item/item.php
@@ -18,7 +18,6 @@ class ItemBaseResponse extends TemplateResponse implements ICache
protected array $breadcrumb = [0, 0];
protected array $scripts = array(
- [SC_JS_FILE, 'js/swfobject.js'],
[SC_JS_FILE, 'js/profile.js'],
[SC_JS_FILE, 'js/filters.js']
);
diff --git a/endpoints/items/items.php b/endpoints/items/items.php
index 97d4a9d1..21aff287 100644
--- a/endpoints/items/items.php
+++ b/endpoints/items/items.php
@@ -19,7 +19,7 @@ class ItemsBaseResponse extends TemplateResponse implements ICache
protected array $breadcrumb = [0, 0];
protected array $dataLoader = ['weight-presets'];
- protected array $scripts = [[SC_JS_FILE, 'js/filters.js'], [SC_JS_FILE, 'js/swfobject.js']];
+ protected array $scripts = [[SC_JS_FILE, 'js/filters.js']];
protected array $expectedGET = array(
'filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]
);
diff --git a/endpoints/itemset/itemset.php b/endpoints/itemset/itemset.php
index 0b017380..398fe55e 100644
--- a/endpoints/itemset/itemset.php
+++ b/endpoints/itemset/itemset.php
@@ -17,7 +17,7 @@ class ItemsetBaseResponse extends TemplateResponse implements ICache
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 2];
- protected array $scripts = [[SC_JS_FILE, 'js/swfobject.js'], [SC_JS_FILE, 'js/Summary.js']];
+ protected array $scripts = [[SC_JS_FILE, 'js/Summary.js']];
public int $type = Type::ITEMSET;
public int $typeId = 0;
diff --git a/endpoints/npc/npc.php b/endpoints/npc/npc.php
index 38de19f9..3d4624e2 100644
--- a/endpoints/npc/npc.php
+++ b/endpoints/npc/npc.php
@@ -17,7 +17,7 @@ class NpcBaseResponse extends TemplateResponse implements ICache
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 4];
- protected array $scripts = [[SC_JS_FILE, 'js/swfobject.js'], [SC_CSS_FILE, 'css/Profiler.css']];
+ protected array $scripts = [[SC_CSS_FILE, 'css/Profiler.css']];
public int $type = Type::NPC;
public int $typeId = 0;
diff --git a/endpoints/object/object.php b/endpoints/object/object.php
index 203fa6a0..9c6a1ba2 100644
--- a/endpoints/object/object.php
+++ b/endpoints/object/object.php
@@ -17,8 +17,6 @@ class ObjectBaseResponse extends TemplateResponse implements ICache
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 5];
- protected array $scripts = [[SC_JS_FILE, 'js/swfobject.js']];
-
public int $type = Type::OBJECT;
public int $typeId = 0;
public ?Book $book = null;
diff --git a/endpoints/pet/pet.php b/endpoints/pet/pet.php
index df2af810..f383a0f7 100644
--- a/endpoints/pet/pet.php
+++ b/endpoints/pet/pet.php
@@ -17,8 +17,6 @@ class PetBaseResponse extends TemplateResponse implements ICache
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 8];
- protected array $scripts = [[SC_JS_FILE, 'js/swfobject.js']];
-
public int $type = Type::PET;
public int $typeId = 0;
public ?string $expansion = null;
diff --git a/endpoints/petcalc/petcalc.php b/endpoints/petcalc/petcalc.php
index 0532008d..cff9539b 100644
--- a/endpoints/petcalc/petcalc.php
+++ b/endpoints/petcalc/petcalc.php
@@ -19,7 +19,6 @@ class PetcalcBaseResponse extends TemplateResponse
[SC_CSS_FILE, 'css/talentcalc.css'],
[SC_CSS_FILE, 'css/talent.css'],
[SC_JS_FILE, 'js/petcalc.js'],
- [SC_JS_FILE, 'js/swfobject.js'],
[SC_CSS_FILE, 'css/petcalc.css']
);
diff --git a/endpoints/profile/profile.php b/endpoints/profile/profile.php
index ee921ccb..284ed283 100644
--- a/endpoints/profile/profile.php
+++ b/endpoints/profile/profile.php
@@ -19,7 +19,6 @@ class ProfileBaseResponse extends TemplateResponse
protected array $scripts = array(
[SC_JS_FILE, 'js/filters.js'],
[SC_JS_FILE, 'js/TalentCalc.js'],
- [SC_JS_FILE, 'js/swfobject.js'],
[SC_JS_FILE, 'js/profile_all.js'],
[SC_JS_FILE, 'js/profile.js'],
[SC_JS_FILE, 'js/Profiler.js'],
diff --git a/endpoints/race/race.php b/endpoints/race/race.php
index 2134bedc..1bb261c6 100644
--- a/endpoints/race/race.php
+++ b/endpoints/race/race.php
@@ -23,8 +23,6 @@ class RaceBaseResponse extends TemplateResponse implements ICache
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 13];
- protected array $scripts = [[SC_JS_FILE, 'js/swfobject.js']];
-
public int $type = Type::CHR_RACE;
public int $typeId = 0;
public ?string $expansion = null;
diff --git a/endpoints/search/search.php b/endpoints/search/search.php
index dc2687cd..edf8cb73 100644
--- a/endpoints/search/search.php
+++ b/endpoints/search/search.php
@@ -23,7 +23,6 @@ class SearchBaseResponse extends TemplateResponse implements ICache
protected string $pageName = 'search';
protected ?int $activeTab = parent::TAB_DATABASE;
- protected array $scripts = [[SC_JS_FILE, 'js/swfobject.js']];
protected array $expectedGET = array(
'search' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']]
);
diff --git a/endpoints/spell/spell.php b/endpoints/spell/spell.php
index 05111983..8f1e6645 100644
--- a/endpoints/spell/spell.php
+++ b/endpoints/spell/spell.php
@@ -23,8 +23,6 @@ class SpellBaseResponse extends TemplateResponse implements ICache
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 1];
- protected array $scripts = [[SC_JS_FILE, 'js/swfobject.js']];
-
public int $type = Type::SPELL;
public int $typeId = 0;
public array $reagents = [false, null];
diff --git a/includes/cfg.class.php b/includes/cfg.class.php
index 138e541d..b82cb1ad 100644
--- a/includes/cfg.class.php
+++ b/includes/cfg.class.php
@@ -48,17 +48,17 @@ class Cfg
private static $isLoaded = false;
private static $rebuildScripts = array(
- // 'rep_req_border_unco' => ['global'], // currently not a template or buildScript
- // 'rep_req_border_rare' => ['global'],
- // 'rep_req_border_epic' => ['global'],
- // 'rep_req_border_lege' => ['global'],
- 'profiler_enable' => ['realms', 'realmMenu'],
- 'battlegroup' => ['realms', 'realmMenu'],
- 'name_short' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'demo'],
- 'site_host' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'demo', 'power'],
- 'static_host' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'power'],
- 'contact_email' => ['markup'],
- 'locales' => ['locales']
+ 'rep_req_border_uncommon' => ['globaljs'],
+ 'rep_req_border_rare' => ['globaljs'],
+ 'rep_req_border_epic' => ['globaljs'],
+ 'rep_req_border_legendary' => ['globaljs'],
+ 'profiler_enable' => ['realms', 'realmMenu'],
+ 'battlegroup' => ['realms', 'realmMenu'],
+ 'name_short' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'demo'],
+ 'site_host' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'demo', 'power'],
+ 'static_host' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'power'],
+ 'contact_email' => ['globaljs'],
+ 'locales' => ['globaljs']
);
public static function load() : void
@@ -294,16 +294,16 @@ class Cfg
yield $k => self::$store[$k];
}
- public static function applyToString(string $string) : string
+ public static function applyToString(string $string, bool $nf = true) : string
{
return preg_replace_callback(
['/CFG_([A-Z_]+)/', '/((HOST|STATIC)_URL)/'],
- function ($m) {
+ function ($m) use ($nf) {
if (!isset(self::$store[strtolower($m[1])]))
return $m[1];
[$val, $flags, , , ] = self::$store[strtolower($m[1])];
- return $flags & (self::FLAG_TYPE_FLOAT | self::FLAG_TYPE_INT) ? Lang::nf($val) : $val;
+ return ($flags & (self::FLAG_TYPE_FLOAT | self::FLAG_TYPE_INT)) && $nf ? Lang::nf($val) : $val;
},
$string
);
diff --git a/includes/components/response/templateresponse.class.php b/includes/components/response/templateresponse.class.php
index bc2cda60..a8760bd4 100644
--- a/includes/components/response/templateresponse.class.php
+++ b/includes/components/response/templateresponse.class.php
@@ -111,8 +111,6 @@ class TemplateResponse extends BaseResponse
[SC_JS_FILE, 'widgets/power.js', SC_FLAG_NO_TIMESTAMP | SC_FLAG_APPEND_LOCALE],
[SC_JS_FILE, 'js/locale_%s.js', SC_FLAG_LOCALIZED ],
[SC_JS_FILE, 'js/global.js' ],
- [SC_JS_FILE, 'js/locale.js' ],
- [SC_JS_FILE, 'js/Markup.js' ],
[SC_CSS_FILE, 'css/basic.css' ],
[SC_CSS_FILE, 'css/global.css' ],
[SC_CSS_FILE, 'css/aowow.css' ],
diff --git a/setup/tools/filegen/locales.ss.php b/setup/tools/filegen/global-js.ss.php
similarity index 86%
rename from setup/tools/filegen/locales.ss.php
rename to setup/tools/filegen/global-js.ss.php
index 4eef9790..28fd1d55 100644
--- a/setup/tools/filegen/locales.ss.php
+++ b/setup/tools/filegen/global-js.ss.php
@@ -9,8 +9,6 @@ if (!CLI)
die('not in cli mode');
-// Create 'locale.js'-file in static/js
-
/*
0: { // English
id: LOCALE_ENUS,
@@ -55,11 +53,13 @@ CLISetup::registerSetup("build", new class extends SetupScript
use TrTemplateFile;
protected $info = array(
- 'locales' => [[], CLISetup::ARGV_PARAM, 'Compiles the Locale Object (static/js/locale.js) with available languages.']
+ 'globaljs' => [[], CLISetup::ARGV_PARAM, 'Compiles the global javascript file (static/js/global.js).']
);
- protected $fileTemplateDest = ['static/js/locale.js'];
- protected $fileTemplateSrc = ['locale.js.in'];
+ protected $fileTemplateDest = ['static/js/global.js'];
+ protected $fileTemplateSrc = ['global.js'];
+
+ private bool $numFmt = false;
private function locales() : string
{
diff --git a/setup/tools/filegen/markup.ss.php b/setup/tools/filegen/markup.ss.php
deleted file mode 100644
index 70ceecba..00000000
--- a/setup/tools/filegen/markup.ss.php
+++ /dev/null
@@ -1,24 +0,0 @@
- [[], CLISetup::ARGV_PARAM, 'Fills the markup parser (static/js/Markup.js) with site variables.']
- );
-
- protected $fileTemplateSrc = ['Markup.js.in'];
- protected $fileTemplateDest = ['static/js/Markup.js'];
-});
-
-?>
diff --git a/setup/tools/filegen/templates/global.js/0_user.js b/setup/tools/filegen/templates/global.js/0_user.js
new file mode 100644
index 00000000..7202a369
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/0_user.js
@@ -0,0 +1,73 @@
+// Needed for IE because it's dumb
+
+'abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video'.replace(/\w+/g,function(n){document.createElement(n)})
+
+
+/*
+User-related functions
+TODO: Move global variables/functions into User class
+*/
+
+// IMPORTANT: If you update/change the permission groups below make sure to also update them in User.inc.php!
+
+/*********/
+/* ROLES */
+/*********/
+
+var U_GROUP_TESTER = 0x1;
+var U_GROUP_ADMIN = 0x2;
+var U_GROUP_EDITOR = 0x4;
+var U_GROUP_MOD = 0x8;
+var U_GROUP_BUREAU = 0x10;
+var U_GROUP_DEV = 0x20;
+var U_GROUP_VIP = 0x40;
+var U_GROUP_BLOGGER = 0x80;
+var U_GROUP_PREMIUM = 0x100;
+var U_GROUP_LOCALIZER = 0x200;
+var U_GROUP_SALESAGENT = 0x400;
+var U_GROUP_SCREENSHOT = 0x800;
+var U_GROUP_VIDEO = 0x1000;
+var U_GROUP_APIONLY = 0x2000;
+var U_GROUP_PENDING = 0x4000;
+
+
+/******************/
+/* ROLE SHORTCUTS */
+/******************/
+
+var U_GROUP_STAFF = U_GROUP_ADMIN | U_GROUP_EDITOR | U_GROUP_MOD | U_GROUP_BUREAU | U_GROUP_DEV | U_GROUP_BLOGGER | U_GROUP_LOCALIZER | U_GROUP_SALESAGENT;
+var U_GROUP_EMPLOYEE = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_DEV;
+var U_GROUP_GREEN_TEXT = U_GROUP_MOD | U_GROUP_BUREAU | U_GROUP_DEV;
+var U_GROUP_PREMIUMISH = U_GROUP_PREMIUM | U_GROUP_EDITOR;
+var U_GROUP_MODERATOR = U_GROUP_ADMIN | U_GROUP_MOD | U_GROUP_BUREAU;
+var U_GROUP_COMMENTS_MODERATOR = U_GROUP_BUREAU | U_GROUP_MODERATOR | U_GROUP_LOCALIZER;
+var U_GROUP_PREMIUM_PERMISSIONS = U_GROUP_PREMIUM | U_GROUP_STAFF | U_GROUP_VIP;
+
+var g_users = {};
+var g_favorites = [];
+var g_customColors = {};
+
+function g_isUsernameValid(username) {
+ return (username.match(/[^a-z0-9]/i) == null && username.length >= 4 && username.length <= 16);
+}
+
+var User = new function() {
+ var self = this;
+
+ /**********/
+ /* PUBLIC */
+ /**********/
+
+ self.hasPermissions = function(roles)
+ {
+ if(!roles)
+ return true;
+
+ return !!(g_user.roles & roles);
+ }
+
+ /**********/
+ /* PRIVATE */
+ /**********/
+
+};
diff --git a/setup/tools/filegen/templates/global.js/ajax.js b/setup/tools/filegen/templates/global.js/ajax.js
new file mode 100644
index 00000000..22761baa
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/ajax.js
@@ -0,0 +1,51 @@
+function Ajax(url, opt)
+{
+ if (!url)
+ return;
+
+ var _;
+
+ try { _ = new XMLHttpRequest() } catch (e)
+ {
+ try { _ = new ActiveXObject("Msxml2.XMLHTTP") } catch (e)
+ {
+ try { _ = new ActiveXObject("Microsoft.XMLHTTP") } catch (e)
+ {
+ if (window.createRequest)
+ _ = window.createRequest();
+ else
+ {
+ alert(LANG.message_ajaxnotsupported);
+ return;
+ }
+ }
+ }
+ }
+
+ this.request = _;
+
+ $WH.cO(this, opt);
+ this.method = this.method || (this.params && 'POST') || 'GET';
+
+ _.open(this.method, url, this.async == null ? true : this.async);
+ _.onreadystatechange = Ajax.onReadyStateChange.bind(this);
+
+ if (this.method.toUpperCase() == 'POST')
+ _.setRequestHeader('Content-Type', (this.contentType || 'application/x-www-form-urlencoded') + '; charset=' + (this.encoding || 'UTF-8'));
+
+ _.send(this.params);
+}
+
+Ajax.onReadyStateChange = function()
+{
+ if (this.request.readyState == 4)
+ {
+ if (this.request.status == 0 || (this.request.status >= 200 && this.request.status < 300))
+ this.onSuccess != null && this.onSuccess(this.request, this);
+ else
+ this.onFailure != null && this.onFailure(this.request, this);
+
+ if (this.onComplete != null)
+ this.onComplete(this.request, this);
+ }
+};
diff --git a/setup/tools/filegen/templates/global.js/animations.js b/setup/tools/filegen/templates/global.js/animations.js
new file mode 100644
index 00000000..a41697ef
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/animations.js
@@ -0,0 +1,123 @@
+/*
+ * jQuery Color Animations
+ * Copyright 2007 John Resig
+ * Released under the MIT and GPL licenses.
+ */
+
+(function(jQuery){
+
+ // We override the animation for all of these color styles
+ jQuery.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){
+ jQuery.fx.step[attr] = function(fx){
+ if ( fx.state == 0 ) {
+ fx.start = getColor( fx.elem, attr );
+ fx.end = getRGB( fx.end );
+ }
+
+ fx.elem.style[attr] = "rgb(" + [
+ Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0),
+ Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0),
+ Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0)
+ ].join(",") + ")";
+ }
+ });
+
+ // Color Conversion functions from highlightFade
+ // By Blair Mitchelmore
+ // http://jquery.offput.ca/highlightFade/
+
+ // Parse strings looking for color tuples [255,255,255]
+ function getRGB(color) {
+ var result;
+
+ // Check if we're already dealing with an array of colors
+ if ( color && color.constructor == Array && color.length == 3 )
+ return color;
+
+ // Look for rgb(num,num,num)
+ if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
+ return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];
+
+ // Look for rgb(num%,num%,num%)
+ if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
+ return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
+
+ // Look for #a0b1c2
+ if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
+ return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
+
+ // Look for #fff
+ if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
+ return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
+
+ // Otherwise, we're most likely dealing with a named color
+ return colors[jQuery.trim(color).toLowerCase()];
+ }
+
+ function getColor(elem, attr) {
+ var color;
+
+ do {
+ color = jQuery.curCSS(elem, attr);
+
+ // Keep going until we find an element that has color, or we hit the body
+ if ( color != '' && color != 'transparent' || jQuery.nodeName(elem, "body") )
+ break;
+
+ attr = "backgroundColor";
+ } while ( elem = elem.parentNode );
+
+ return getRGB(color);
+ };
+
+ // Some named colors to work with
+ // From Interface by Stefan Petre
+ // http://interface.eyecon.ro/
+
+ var colors = {
+ aqua:[0,255,255],
+ azure:[240,255,255],
+ beige:[245,245,220],
+ black:[0,0,0],
+ blue:[0,0,255],
+ brown:[165,42,42],
+ cyan:[0,255,255],
+ darkblue:[0,0,139],
+ darkcyan:[0,139,139],
+ darkgrey:[169,169,169],
+ darkgreen:[0,100,0],
+ darkkhaki:[189,183,107],
+ darkmagenta:[139,0,139],
+ darkolivegreen:[85,107,47],
+ darkorange:[255,140,0],
+ darkorchid:[153,50,204],
+ darkred:[139,0,0],
+ darksalmon:[233,150,122],
+ darkviolet:[148,0,211],
+ fuchsia:[255,0,255],
+ gold:[255,215,0],
+ green:[0,128,0],
+ indigo:[75,0,130],
+ khaki:[240,230,140],
+ lightblue:[173,216,230],
+ lightcyan:[224,255,255],
+ lightgreen:[144,238,144],
+ lightgrey:[211,211,211],
+ lightpink:[255,182,193],
+ lightyellow:[255,255,224],
+ lime:[0,255,0],
+ magenta:[255,0,255],
+ maroon:[128,0,0],
+ navy:[0,0,128],
+ olive:[128,128,0],
+ orange:[255,165,0],
+ pink:[255,192,203],
+ purple:[128,0,128],
+ violet:[128,0,128],
+ red:[255,0,0],
+ silver:[192,192,192],
+ white:[255,255,255],
+ yellow:[255,255,0]
+ };
+
+})(jQuery);
diff --git a/setup/tools/filegen/templates/global.js/announcement.js b/setup/tools/filegen/templates/global.js/announcement.js
new file mode 100644
index 00000000..0149b984
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/announcement.js
@@ -0,0 +1,157 @@
+var Announcement = function(opt)
+{
+ if (!opt)
+ opt = {};
+
+ $WH.cO(this, opt);
+
+ if (this.parent)
+ this.parentDiv = $WH.ge(this.parent);
+ else
+ return;
+
+ if (g_user.id > 0 && (!g_cookiesEnabled() || g_getWowheadCookie('announcement-' + this.id) == 'closed'))
+ return;
+
+ this.initialize();
+};
+
+Announcement.prototype = {
+ initialize: function()
+ {
+ // aowow - animation fix
+ // this.parentDiv.style.display = 'none';
+ this.parentDiv.style.opacity = '0';
+
+ if (this.mode === undefined || this.mode == 1)
+ this.parentDiv.className = 'announcement announcement-contenttop';
+ else
+ this.parentDiv.className = 'announcement announcement-pagetop';
+
+ var div = this.innerDiv = $WH.ce('div');
+ div.className = 'announcement-inner text';
+ this.setStyle(this.style);
+
+ var a = null;
+ var id = parseInt(this.id);
+
+ if (g_user && (g_user.roles & (U_GROUP_ADMIN|U_GROUP_BUREAU)) > 0 && Math.abs(id) > 0)
+ {
+ if (id < 0)
+ {
+ a = $WH.ce('a');
+ a.style.cssFloat = a.style.styleFloat = 'right';
+ a.href = '?admin=announcements&id=' + Math.abs(id) + '&status=2';
+ a.onclick = function() { return confirm('Are you sure you want to delete ' + this.name + '?'); };
+ $WH.ae(a, $WH.ct('Delete'));
+ var small = $WH.ce('small');
+ $WH.ae(small, a);
+ $WH.ae(div, small);
+
+ a = $WH.ce('a');
+ a.style.cssFloat = a.style.styleFloat = 'right';
+ a.style.marginRight = '10px';
+ a.href = '?admin=announcements&id=' + Math.abs(id) + '&status=' + (this.status == 1 ? 0 : 1);
+ a.onclick = function() { return confirm('Are you sure you want to delete ' + this.name + '?'); };
+ $WH.ae(a, $WH.ct((this.status == 1 ? 'Disable' : 'Enable')));
+ var small = $WH.ce('small');
+ $WH.ae(small, a);
+ $WH.ae(div, small);
+ }
+
+ a = $WH.ce('a');
+ a.style.cssFloat = a.style.styleFloat = 'right';
+ a.style.marginRight = '22px';
+ a.href = '?admin=announcements&id=' + Math.abs(id) + '&edit';
+ $WH.ae(a, $WH.ct('Edit announcement'));
+ var small = $WH.ce('small');
+ $WH.ae(small, a);
+ $WH.ae(div, small);
+ }
+
+ var markupDiv = $WH.ce('div');
+ markupDiv.id = this.parent + '-markup';
+ $WH.ae(div, markupDiv);
+
+ if (id >= 0)
+ {
+ a = $WH.ce('a');
+
+ a.id = 'closeannouncement';
+ a.href = 'javascript:;';
+ a.className = 'announcement-close';
+ if (this.nocookie)
+ a.onclick = this.hide.bind(this);
+ else
+ a.onclick = this.markRead.bind(this);
+
+ $WH.ae(div, a);
+ g_addTooltip(a, LANG.close);
+ }
+
+ $WH.ae(div, $WH.ce('div', { style: { clear: 'both' } }));
+
+ $WH.ae(this.parentDiv, div);
+
+ this.setText(this.text);
+
+ setTimeout(this.show.bind(this), 500); // Delay to avoid visual lag
+ },
+
+ show: function()
+ {
+ // $(this.parentDiv).animate({
+ // opacity: 'show',
+ // height: 'show'
+ // },{
+ // duration: 333
+ // });
+
+ // aowow - animation fix - jQuery.animate hard snaps into place after half the time passed
+ this.parentDiv.style.opacity = '100';
+ this.parentDiv.style.height = (this.parentDiv.offsetHeight + 10) + 'px';
+ g_trackEvent('Announcements', 'Show', '' + this.name);
+ },
+
+ hide: function()
+ {
+ // $(this.parentDiv).animate({
+ // opacity: 'hide',
+ // height: 'hide'
+ // },{
+ // duration: 200
+ // });
+
+ // aowow - animation fix - jQuery.animate hard snaps into place after half the time passed
+ this.parentDiv.style.opacity = '0';
+ this.parentDiv.style.height = '0px';
+ setTimeout(function() {
+ this.parentDiv.style.display = 'none';
+ }.bind(this), 400);
+ },
+
+ markRead: function()
+ {
+ g_trackEvent('Announcements', 'Close', '' + this.name);
+ g_setWowheadCookie('announcement-' + this.id, 'closed');
+ this.hide();
+ },
+
+ setStyle: function(style)
+ {
+ this.style = style;
+ this.innerDiv.setAttribute('style', style);
+ },
+
+ setText: function(text)
+ {
+ this.text = text;
+ Markup.printHtml(this.text, this.parent + '-markup');
+ g_addAnalyticsToNode($WH.ge(this.parent + '-markup'), {
+ 'category': 'Announcements',
+ 'actions': {
+ 'Follow link': function(node) { return true; }
+ }
+ }, this.id);
+ }
+};
diff --git a/setup/tools/filegen/templates/global.js/audio.js b/setup/tools/filegen/templates/global.js/audio.js
new file mode 100644
index 00000000..106a93c4
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/audio.js
@@ -0,0 +1,377 @@
+var g_audiocontrols = {
+ __windowloaded: false,
+};
+var g_audioplaylist = {};
+
+// aowow - why is window.JSON here, wedged between the audio controls. It's only used for SearchBrowseButtons (and sourced by Listview)
+if (!window.JSON) {
+ window.JSON = {
+ parse: function (sJSON) {
+ return eval("(" + sJSON + ")");
+ },
+
+ stringify: function (obj) {
+ if (obj instanceof Object)
+ {
+ var str = '';
+ if (obj.constructor === Array)
+ {
+ for (var i = 0; i < obj.length; str += this.stringify(obj[i]) + ',', i++) {}
+ return '[' + str.substr(0, str.length - 1) + ']';
+ }
+ if (obj.toString !== Object.prototype.toString)
+ return '"' + obj.toString().replace(/"/g, '\\$&') + '"';
+
+ for (var e in obj)
+ str += '"' + e.replace(/"/g, '\\$&') + '":' + this.stringify(obj[e]) + ',';
+
+ return '{' + str.substr(0, str.length - 1) + '}';
+ }
+
+ return typeof obj === 'string' ? '"' + obj.replace(/"/g, '\\$&') + '"' : String(obj);
+ }
+ }
+}
+
+AudioControls = function ()
+{
+ var fileIdx = -1;
+ var canPlay = false;
+ var looping = false;
+ var fullPlayer = false;
+ var autoStart = false;
+ var controls = {};
+ var playlist = [];
+ var url = '';
+
+ function updatePlayer(_self, itr, doPlay)
+ {
+ var elAudio = $WH.ce('audio');
+ elAudio.preload = 'none';
+ elAudio.controls = 'true';
+ $(elAudio).click(function (s) { s.stopPropagation() });
+ elAudio.style.marginTop = '5px';
+
+ controls.audio.parentNode.replaceChild(elAudio, controls.audio);
+ controls.audio = elAudio;
+ $WH.aE(controls.audio, 'ended', setNextTrack.bind(_self));
+
+ if (doPlay)
+ {
+ elAudio.preload = 'auto';
+ autoStart = true;
+ $WH.aE(controls.audio, 'canplaythrough', autoplay.bind(this));
+ }
+
+ if (!canPlay)
+ controls.table.style.visibility = 'visible';
+
+ var file;
+ do
+ {
+ fileIdx += itr;
+ if (fileIdx > playlist.length - 1)
+ {
+ fileIdx = 0;
+ if (!canPlay)
+ {
+ var div = $WH.ce('div');
+ // div.className = 'minibox'; Aowow custom
+ div.className = 'minibox minibox-left';
+ $WH.st(div, $WH.sprintf(LANG.message_browsernoaudio, file.type));
+ controls.table.parentNode.replaceChild(div, controls.table);
+ return
+ }
+ }
+
+ if (fileIdx < 0)
+ fileIdx = playlist.length - 1;
+
+ file = playlist[fileIdx];
+ }
+ while (controls.audio.canPlayType(file.type) == '');
+
+ var elSource = $WH.ce('source');
+ elSource.src = file.url;
+ elSource.type = file.type;
+ $WH.ae(controls.audio, elSource);
+
+ if (controls.hasOwnProperty('title'))
+ {
+ if (url)
+ {
+ $WH.ee(controls.title);
+ var a = $WH.ce('a');
+ a.href = url;
+ $WH.st(a, '"' + file.title + '"');
+ $WH.ae(controls.title, a);
+ }
+ else
+ $WH.st(controls.title, '"' + file.title + '"');
+ }
+
+ if (controls.hasOwnProperty('trackdisplay'))
+ $WH.st(controls.trackdisplay, '' + (fileIdx + 1) + ' / ' + playlist.length);
+
+ if (!canPlay)
+ {
+ canPlay = true;
+ for (var i = fileIdx + 1; i <= playlist.length - 1; i++)
+ {
+ if (controls.audio.canPlayType(playlist[i].type))
+ {
+ $(controls.controlsdiv).children('a').removeClass('button-red-disabled');
+ break;
+ }
+ }
+ }
+
+ if (controls.hasOwnProperty('addbutton'))
+ {
+ $(controls.addbutton).removeClass('button-red-disabled');
+ // $WH.st(controls.addbutton, LANG.add); Aowow: doesnt work with RedButtons
+ RedButton.setText(controls.addbutton, LANG.add);
+ }
+ }
+
+ function autoplay()
+ {
+ if (!autoStart)
+ return;
+
+ autoStart = false;
+ controls.audio.play();
+ }
+
+ this.init = function (files, parent, opt)
+ {
+ if (!$WH.is_array(files))
+ return;
+
+ if (files.length == 0)
+ return;
+
+ if ((parent.id == '') || g_audiocontrols.hasOwnProperty(parent.id))
+ {
+ var i = 0;
+ while (g_audiocontrols.hasOwnProperty('auto-audiocontrols-' + (++i))) {}
+ parent.id = 'auto-audiocontrols-' + i;
+ }
+
+ g_audiocontrols[parent.id] = this;
+
+ if (typeof opt == 'undefined')
+ opt = {};
+
+ looping = !!opt.loop;
+ if (opt.hasOwnProperty('url'))
+ url = opt.url;
+
+ playlist = files;
+ controls.div = parent;
+
+ if (!opt.listview)
+ {
+ var tbl = $WH.ce('table', { className: 'audio-controls' });
+ controls.table = tbl;
+ controls.table.style.visibility = 'hidden';
+ $WH.ae(controls.div, tbl);
+
+ var tr = $WH.ce('tr');
+ $WH.ae(tbl, tr);
+
+ var td = $WH.ce('td');
+ $WH.ae(tr, td);
+
+ controls.audio = $WH.ce('div');
+ $WH.ae(td, controls.audio);
+
+ controls.title = $WH.ce('div', { className: 'audio-controls-title' });
+ $WH.ae(td, controls.title);
+
+ controls.controlsdiv = $WH.ce('div', { className: 'audio-controls-pagination' });
+ $WH.ae(td, controls.controlsdiv);
+
+ var prevBtn = createButton(LANG.previous, true);
+ $WH.ae(controls.controlsdiv, prevBtn);
+ $WH.aE(prevBtn, 'click', this.btnPrevTrack.bind(this));
+
+ controls.trackdisplay = $WH.ce('div', { className: 'audio-controls-pagination-track' });
+ $WH.ae(controls.controlsdiv, controls.trackdisplay);
+
+ var nextBtn = createButton(LANG.next, true);
+ $WH.ae(controls.controlsdiv, nextBtn);
+ $WH.aE(nextBtn, 'click', this.btnNextTrack.bind(this))
+ }
+ else
+ {
+ fullPlayer = true;
+ var div = $WH.ce('div');
+ controls.table = div;
+ $WH.ae(controls.div, div);
+
+ controls.audio = $WH.ce('div');
+ $WH.ae(div, controls.audio);
+
+ controls.trackdisplay = opt.trackdisplay;
+ controls.controlsdiv = $WH.ce('span');
+ $WH.ae(div, controls.controlsdiv);
+ }
+
+ if (g_audioplaylist.isEnabled() && !opt.fromplaylist)
+ {
+ var addBtn = createButton(LANG.add);
+ $WH.ae(controls.controlsdiv, addBtn);
+ $WH.aE(addBtn, 'click', this.btnAddToPlaylist.bind(this, addBtn));
+ controls.addbutton = addBtn;
+
+ if (fullPlayer)
+ addBtn.style.verticalAlign = '50%';
+ }
+
+ if (g_audiocontrols.__windowloaded)
+ this.btnNextTrack();
+ };
+
+ function setNextTrack()
+ {
+ updatePlayer(this, 1, (looping || (fileIdx < (playlist.length - 1))));
+ }
+
+ this.btnNextTrack = function ()
+ {
+ updatePlayer(this, 1, (canPlay && (controls.audio.readyState > 1) && (!controls.audio.paused)));
+ };
+
+ this.btnPrevTrack = function ()
+ {
+ updatePlayer(this, -1, (canPlay && (controls.audio.readyState > 1) && (!controls.audio.paused)));
+ };
+
+ this.btnAddToPlaylist = function (_self)
+ {
+ if (fullPlayer)
+ {
+ for (var i = 0; i < playlist.length; i++)
+ g_audioplaylist.addSound(playlist[i]);
+ }
+ else
+ g_audioplaylist.addSound(playlist[fileIdx]);
+
+ _self.className += ' button-red-disabled';
+ // $WH.st(_self, LANG.added); // Aowow doesn't work with RedButtons
+ RedButton.setText(_self, LANG.added);
+ };
+
+ this.isPlaying = function ()
+ {
+ return !controls.audio.paused;
+ };
+
+ this.removeSelf = function ()
+ {
+ controls.table.parentNode.removeChild(controls.table);
+ delete g_audiocontrols[controls.div];
+ };
+
+ function createButton(text, disabled)
+ {
+ return $WH.g_createButton(text, null, {
+ disabled: disabled,
+ // 'float': false, Aowow - adapted style
+ // style: 'margin:0 12px; display:inline-block'
+ style: 'margin:0 12px; display:inline-block; float:inherit; '
+ });
+ }
+};
+
+$WH.aE(window, 'load', function ()
+{
+ g_audiocontrols.__windowloaded = true;
+ for (var i in g_audiocontrols)
+ if (i.substr(0, 2) != '__')
+ g_audiocontrols[i].btnNextTrack();
+});
+
+AudioPlaylist = function ()
+{
+ var enabled = false;
+ var playlist = [];
+ var player, container;
+
+ this.init = function ()
+ {
+ if (!$WH.localStorage.isSupported())
+ return;
+
+ enabled = true;
+
+ var tracks;
+ if (tracks = $WH.localStorage.get('AudioPlaylist'))
+ playlist = JSON.parse(tracks);
+ };
+
+ this.savePlaylist = function ()
+ {
+ if (!enabled)
+ return false;
+
+ $WH.localStorage.set('AudioPlaylist', JSON.stringify(playlist));
+ };
+
+ this.isEnabled = function ()
+ {
+ return enabled;
+ };
+
+ this.addSound = function (track)
+ {
+ if (!enabled)
+ return false;
+
+ this.init();
+ playlist.push(track);
+ this.savePlaylist();
+ };
+
+ this.deleteSound = function (idx)
+ {
+ if (idx < 0)
+ playlist = [];
+ else
+ playlist.splice(idx, 1);
+
+ this.savePlaylist();
+
+ if (!player.isPlaying())
+ {
+ player.removeSelf();
+ this.setAudioControls(container);
+ }
+
+ if (playlist.length == 0)
+ $WH.Tooltip.hide();
+ };
+
+ this.getList = function ()
+ {
+ var buf = [];
+ for (var i = 0; i < playlist.length; i++)
+ buf.push(playlist[i].title);
+
+ return buf;
+ };
+
+ this.setAudioControls = function (parent)
+ {
+ if (!enabled)
+ return false;
+
+ container = parent;
+ player = new AudioControls();
+ player.init(playlist, container, { loop: true, fromplaylist: true });
+ };
+};
+
+g_audioplaylist = (new AudioPlaylist);
+g_audioplaylist.init();
diff --git a/setup/tools/filegen/templates/global.js/clicktocopy.js b/setup/tools/filegen/templates/global.js/clicktocopy.js
new file mode 100644
index 00000000..9225b6f4
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/clicktocopy.js
@@ -0,0 +1,122 @@
+$WH.clickToCopy = function (el, textOrFn, opt)
+{
+ opt = opt || {};
+
+ $WH.aE(el, 'click', $WH.clickToCopy.copy.bind(null, el, textOrFn, opt));
+ // $WH.preventSelectStart(el);
+
+ el.classList.add('click-to-copy');
+
+ if (opt.modifyTooltip)
+ {
+ el._fixTooltip = function (e) {
+ return e + '
' + $WH.ce('span', { className: 'q2', innerHTML: $WH.clickToCopy.getTooltip(false, opt) }).outerHTML;
+ };
+
+ opt.overrideOtherTooltips = false;
+ }
+
+ // aowow - fitted to old system
+ // $WH.Tooltips.attach(
+ $WH.Tooltip.simple(
+ el,
+ $WH.clickToCopy.getTooltip.bind(null, false, opt),
+ undefined,
+ // {
+ /* byCursor: */ !opt.attachToElement,
+ // stopPropagation: opt.overrideOtherTooltips
+ // }
+ );
+};
+
+$WH.clickToCopy.copy = function (el, textOrFn, opt, ev)
+{
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ if (textOrFn === undefined)
+ {
+ if (!el.childNodes[0] || !el.childNodes[0].textContent)
+ {
+ let text = 'Could not find text to copy.';
+ // $WH.error(text, el);
+
+ if (opt.attachToElement)
+ $WH.Tooltip.show(el, text, 'q10');
+ else
+ $WH.Tooltip.showAtCursor(ev, text, 'q10');
+
+ return;
+ }
+
+ textOrFn = el.childNodes[0].textContent;
+ }
+ else if (typeof textOrFn === 'function')
+ textOrFn = textOrFn();
+
+ $WH.copyToClipboard(textOrFn);
+
+ if (opt.attachToElement)
+ $WH.Tooltip.show(el, $WH.clickToCopy.getTooltip(true, opt));
+ else
+ $WH.Tooltip.showAtCursor(ev, $WH.clickToCopy.getTooltip(true, opt));
+};
+
+$WH.clickToCopy.getTooltip = function (clicked, opt)
+{
+ let txt = '';
+ let attr = undefined;
+
+ if (clicked)
+ {
+ txt = ' ' + LANG.copied;
+ attr = { className: 'q1 icon-tick' };
+ }
+ else
+ txt = LANG.clickToCopy;
+
+ let tt = $WH.ce('div', attr, $WH.ct(txt));
+
+ if (opt.prefix)
+ {
+ tt.style.marginTop = '10px';
+ let prefix = typeof opt.prefix === 'function' ? opt.prefix() : opt.prefix;
+ return prefix + tt.outerHTML;
+ }
+
+ return tt.outerHTML;
+};
+
+$WH.copyToClipboard = function (text, t)
+{
+ if (!$WH.copyToClipboard.hiddenInput)
+ {
+ $WH.copyToClipboard.hiddenInput = $WH.ce('textarea', { className: 'hidden-element' });
+ $WH.ae(document.body, $WH.copyToClipboard.hiddenInput);
+ }
+
+ $WH.copyToClipboard.hiddenInput.value = text;
+
+ let isEmpty = $WH.copyToClipboard.hiddenInput.value === '';
+ if (isEmpty)
+ $WH.copyToClipboard.hiddenInput.value = LANG.nothingToCopy_tip;
+
+ $WH.copyToClipboard.hiddenInput.focus();
+ $WH.copyToClipboard.hiddenInput.select();
+
+ if (!document.execCommand('copy'))
+ prompt(null, text);
+
+ $WH.copyToClipboard.hiddenInput.blur();
+
+ if (t)
+ {
+ if (isEmpty)
+ $WH.Tooltips.showFadingTooltipAtCursor(LANG.nothingToCopy_tip, t, 'q10');
+ else
+ {
+ let e = $WH.ce('span', { className: 'q1 icon-tick' }, $WH.ct(' ' + LANG.copied));
+ $WH.Tooltips.showFadingTooltipAtCursor(e.outerHTML, t);
+ }
+ }
+};
diff --git a/setup/tools/filegen/templates/global.js/comments.js b/setup/tools/filegen/templates/global.js/comments.js
new file mode 100644
index 00000000..352a6be2
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/comments.js
@@ -0,0 +1,459 @@
+/* Note: comment replies are called "comments" because part of this code was taken from another project of mine. */
+
+function SetupReplies(post, comment)
+{
+ SetupAddEditComment(post, comment, false);
+ SetupShowMoreComments(post, comment);
+
+ post.find('.comment-reply-row').each(function () { SetupRepliesControls($(this), comment); });
+ post.find('.comment-reply-row').hover(function () { $(this).find('span').attr('data-hover', 'true'); }, function () { $(this).find('span').attr('data-hover', 'false'); });
+}
+
+function SetupAddEditComment(post, comment, edit)
+{
+ /* Variables that will be set by Initialize() */
+ var Form = null;
+ var Body = null;
+ var AddButton = null;
+ var TextCounter = null;
+ var AjaxLoader = null;
+ var FormContainer = null;
+ var DialogTableRowContainer = null;
+
+ /* Constants */
+ var MIN_LENGTH = 15;
+ var MAX_LENGTH = 600;
+
+ /* State keeping booleans */
+ var Initialized = false;
+ var Active = false;
+ var Flashing = false;
+ var Submitting = false;
+
+ /* Shortcuts */
+ var CommentsTable = post.find('.comment-replies > table');
+ var AddCommentLink = post.find('.add-reply');
+ var CommentsCount = comment.replies.length;
+
+ if(edit)
+ Open();
+ else
+ AddCommentLink.click(function () { Open(); });
+
+ function Initialize()
+ {
+ if (Initialized)
+ return;
+
+ Initialized = true;
+
+ var row = $('