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 = $(''); + + if(edit) + row.addClass('comment-reply-row').addClass('reply-edit-row'); + + row.html('' + + '
' + + '' + + '
' + + '' + + '' + + '' + + '
' + + '' + + '' + + '' + + '' + + '
' + + 'Text counter placeholder' + + '
' + + '
' + + ''); + + /* Set up the various variables for the controls we just created */ + Body = row.find('.comment-form textarea'); + AddButton = row.find('.comment-form input[type=submit]'); + TextCounter = row.find('.comment-form span.text-counter'); + Form = row.find('.comment-form form'); + AjaxLoader = row.find('.comment-form .ajax-loader'); + FormContainer = row.find('.comment-form'); + + /* Intercept submits */ + Form.submit(function () { Submit(); return false; }); + + UpdateTextCounter(); + + /* This is kinda a mess.. Every browser seems to implement keyup, keydown and keypress differently. + * - keyup: We need to use keyup to update the text counter for the simple reason we want to update it only when the user stops typing. + * - keydown: We need to use keydown to detect the ESC key because it's the only one that works in all browsers for ESC + * - keypress: We need to use keypress to detect Enter because it's the only one that 1) Works 2) Allows us to prevent a new line from being entered in the textarea + * I find it very funny that in each scenario there is only one of the 3 that works, and that that one is always different from the others. + */ + + Body.keyup(function (e) { UpdateTextCounter(); }); + Body.keydown(function (e) { if (e.keyCode == 27) { Close(); return false; } }); // ESC + Body.keypress(function (e) { if (e.keyCode == 13) { Submit(); return false; } }); // ENTER + + if(edit) + { + post.after(row); + post.hide(); + Form.find('textarea').text(comment.replies[post.attr('data-idx')].body); + } + else + CommentsTable.append(row); + + DialogTableRowContainer = row; + Form.find('textarea').focus(); + } + + function Open() + { + if (!Initialized) + Initialize(); + + Active = true; + + if(!edit) + { + AddCommentLink.hide(); + post.find('.comment-replies').show(); + FormContainer.show(); + FormContainer.find('textarea').focus(); + } + } + + function Close() + { + Active = false; + + if(edit) + { + if(DialogTableRowContainer) + DialogTableRowContainer.remove(); + post.show(); + return; + } + + AddCommentLink.show(); + FormContainer.hide(); + + if (CommentsCount == 0) + post.find('.comment-replies').hide(); + } + + function Submit() + { + if (!Active || Submitting) + return; + + if (Body.val().length < MIN_LENGTH || Body.val().length > MAX_LENGTH) + { + /* Flash the char counter to attract the attention of the user. */ + if (!Flashing) + { + Flashing = true; + TextCounter.animate({ opacity: '0.0' }, 150); + TextCounter.animate({ opacity: '1.0' }, 150, null, function() { Flashing = false; }); + } + + return false; + } + + SetSubmitState(); + $.ajax({ + type: 'POST', + url: edit ? '?comment=edit-reply' : '?comment=add-reply', + data: { commentId: comment.id, replyId: (edit ? post.attr('data-replyid') : 0), body: Body.val() }, + success: function (newReplies) { OnSubmitSuccess(newReplies); }, + dataType: 'json', + error: function (jqXHR) { OnSubmitFailure(jqXHR.responseText); } + }); + return true; + } + + function SetSubmitState() + { + Submitting = true; + AjaxLoader.show(); + AddButton.attr('disabled', 'disabled'); + FormContainer.find('.message-box').remove(); + } + + function ClearSubmitState() + { + Submitting = false; + AjaxLoader.hide(); + AddButton.removeAttr('disabled'); + } + + function OnSubmitSuccess(newReplies) + { + comment.replies = newReplies; + Listview.templates.comment.updateReplies(comment); + } + + function OnSubmitFailure(error) + { + ClearSubmitState(); + MessageBox(FormContainer, error); + } + + function UpdateTextCounter() + { + var text = '(error)'; + var cssClass = 'q0'; + var chars = Body.val().replace(/(\s+)/g, ' ').replace(/^\s*/, '').replace(/\s*$/, '').length; + var charsLeft = MAX_LENGTH - chars; + + if (chars == 0) + text = $WH.sprintf(LANG.replylength1_format, MIN_LENGTH); + else if (chars < MIN_LENGTH) + text = $WH.sprintf(LANG.replylength2_format, MIN_LENGTH - chars); + else + { + text = $WH.sprintf(charsLeft == 1 ? LANG.replylength4_format : LANG.replylength3_format, charsLeft); + + if (charsLeft < 120) + cssClass = 'q10'; + else if (charsLeft < 240) + cssClass = 'q5'; + else if (charsLeft < 360) + cssClass = 'q11'; + } + + TextCounter.html(text).attr('class', cssClass); + } +} + +function SetupShowMoreComments(post, comment) +{ + var ShowMoreCommentsLink = post.find('.show-more-replies'); + var CommentCell = post.find('.comment-replies'); + + ShowMoreCommentsLink.click(function () { ShowMoreComments(); }); + + function ShowMoreComments() + { + /* Replace link with ajax loader */ + ShowMoreCommentsLink.hide(); + CommentCell.append(CreateAjaxLoader()); + + $.ajax({ + type: 'GET', + url: '?comment=show-replies', + data: { id: comment.id }, + success: function (replies) { comment.replies = replies; Listview.templates.comment.updateReplies(comment); }, + dataType: 'json', + error: function () { OnFetchFail(); } + }); + } + + function OnFetchFail() + { + ShowMoreCommentsLink.show(); + CommentCell.find('.ajax-loader').remove(); + + MessageBox(CommentCell, "There was an error fetching the comments. Try refreshing the page."); + } +} + +function SetupRepliesControls(post, comment) +{ + var CommentId = post.attr('data-replyid'); + var VoteUpControl = post.find('.reply-upvote'); + var VoteDownControl = post.find('.reply-downvote'); + var FlagControl = post.find('.reply-report'); + var CommentScoreText = post.find('.reply-rating'); + var CommentActions = post.find('.reply-controls'); + var DeleteButton = post.find('.reply-delete'); + var EditButton = post.find('.reply-edit'); + var Voting = false; + var Deleting = false; + // aowow - detach functionality is custom + var Detaching = false; + var DetachButton = post.find('.reply-detach'); + var Container = comment.repliesCell; + + EditButton.click(function() { + SetupAddEditComment(post, comment, true); + }); + + FlagControl.click(function () + { + if (Voting || !confirm(LANG.replyreportwarning_tip)) + return; + + Voting = true; + $.ajax({ + type: 'POST', + url: '?comment=flag-reply', + data: { id: CommentId }, + success: function () { OnFlagSuccessful(); }, + error: function (jqXHR) { OnError(jqXHR.responseText); } + }); + }); + + VoteUpControl.click(function () + { + if (VoteUpControl.attr('data-hasvoted') == 'true' || VoteUpControl.attr('data-canvote') != 'true' || Voting) + return; + + Voting = true; + $.ajax({ + type: 'POST', + url: '?comment=upvote-reply', + data: { id: CommentId }, + success: function () { OnVoteSuccessful(1); }, + error: function (jqXHR) { OnError(jqXHR.responseText); } + }); + }); + + VoteDownControl.click(function () + { + if (VoteDownControl.attr('data-hasvoted') == 'true' || VoteDownControl.attr('data-canvote') != 'true' || Voting) + return; + + Voting = true; + $.ajax({ + type: 'POST', + url: '?comment=downvote-reply', + data: { id: CommentId }, + success: function () { OnVoteSuccessful(-1); }, + error: function (jqXHR) { OnError(jqXHR.responseText); } + }); + }); + + DetachButton.click(function () + { + if (Detaching) { + MessageBox(CommentActions, LANG.message_cantdetachcomment); + return; + } + + if (!confirm(LANG.confirm_detachcomment)) { + return; + } + + Detaching = true; + $.ajax({ + type: 'POST', + url: '?comment=detach-reply', + data: { id: CommentId }, + success: function () { OnDetachSuccessful(); }, + error: function (jqXHR) { OnError(jqXHR.responseText); } + }); + }); + + DeleteButton.click(function () + { + if (Deleting) + return; + + if (!confirm(LANG.deletereplyconfirmation_tip)) + return; + + Deleting = true; + $.ajax({ + type: 'POST', + url: '?comment=delete-reply', + data: { id: CommentId }, + success: function () { OnDeleteSuccessful(); }, + error: function (jqXHR) { OnError(jqXHR.responseText); } + }); + }); + + function OnVoteSuccessful(ratingChange) + { + var rating = parseInt(CommentScoreText.text()); + + rating += ratingChange; + + CommentScoreText.text(rating); + + if(ratingChange > 0) + VoteUpControl.attr('data-hasvoted', 'true'); + else + VoteDownControl.attr('data-hasvoted', 'true'); + + VoteUpControl.attr('data-canvote', 'false'); + VoteDownControl.attr('data-canvote', 'false'); + + if(ratingChange > 0) + FlagControl.remove(); + Voting = false; + } + + function OnFlagSuccessful() + { + Voting = false; + FlagControl.remove(); + } + + function OnDetachSuccessful() + { + post.remove(); + MessageBox(Container, LANG.message_commentdetached); + Detaching = false; + } + + function OnDeleteSuccessful() + { + post.remove(); + Deleting = false; + } + + function OnError(text) + { + Voting = false; + Deleting = false; + Detaching = false; + + if (!text) + text = LANG.genericerror; + + MessageBox(CommentActions, text); + } +} + +/* +Global comment-related functions +*/ + +function co_addYourComment() +{ + tabsContribute.focus(0); + var ta = $WH.gE(document.forms['addcomment'], 'textarea')[0]; + ta.focus(); +} + +function co_validateForm(f) +{ + var ta = $WH.gE(f, 'textarea')[0]; + + // prevent locale comments on guide pages + var locale = Locale.getId(); + // aowow - disabled + // if(locale != LOCALE_ENUS && $(f).attr('action') && ($(f).attr('action').replace(/^.*type=([0-9]*).*$/i, '$1')) == 100) + if (false) + { + alert(LANG.message_cantpostlcomment_tip); + return false; + } + + if (g_user.permissions & 1) { + return true; + } + + if (Listview.funcBox.coValidate(ta)) { + return true; + } + + return false; +} + +// Display a warning if a user attempts to leave the page and he has started writing a message +$(document).ready(function() +{ + g_setupChangeWarning($("form[name=addcomment]"), [$("textarea[name=commentbody]")], LANG.message_startedpost); +}); diff --git a/setup/tools/filegen/templates/global.js/conditionList.js b/setup/tools/filegen/templates/global.js/conditionList.js new file mode 100644 index 00000000..c788c24c --- /dev/null +++ b/setup/tools/filegen/templates/global.js/conditionList.js @@ -0,0 +1,329 @@ +/* aowow - custom: TrinityCore Conditions */ +var ConditionList = new function() { + var self = this, + _conditions = null; + + self.createCell = function(conditions) + { + if (!conditions) + return null; + + _conditions = conditions; + + return _createCell(); + }; + + self.createTab = function(conditions) + { + if (!conditions) + return null; + + _conditions = conditions; + + return _createTab(); + }; + + function _makeList(mask, src, tpl) + { + var arr = Listview.funcBox.assocBinFlags(mask, src).sort(), + buff = ''; + + for (var i = 0, len = arr.length; i < len; ++i) + { + if (len > 1 && i == len - 1) + buff += LANG.or; + else if (i > 0) + buff += LANG.comma; + + buff += $WH.sprintf(tpl, arr[i], src[arr[i]]); + } + + return buff; + } + + function _parseEntry(entry, targets, target) + { + var str = '', + negate = false, + strIdx = 0, + param = []; + + [strIdx, ...param] = entry; + + negate = strIdx < 0; + strIdx = Math.abs(strIdx); + + if (!g_conditions[strIdx]) + return 'unknown condition index #' + strIdx; + + switch (strIdx) + { + case 5: + var standings = {}; + for (let i in g_reputation_standings) + standings[i * 1 + 1] = g_reputation_standings[i]; + + param[1] = _makeList(entry[2], standings, '$2'); + break; + + case 6: + if (entry[1] == 1) + param[0] = $WH.sprintf('[span class=icon-alliance]$1[/span]', g_sides[1]); + else if (entry[1] == 2) + param[0] = $WH.sprintf('[span class=icon-horde]$1[/span]', g_sides[2]); + else + param[0] = $WH.sprintf('[span class=icon-alliance]$1[/span]$2[span class=icon-horde]$3[/span]', g_sides[1], LANG.or, g_sides[2]); + break; + + case 10: + param[0] = g_drunk_states[entry[1]] ?? 'UNK DRUNK STATE'; + break; + + case 13: + param[2] = g_instance_info[entry[3]] ?? 'UNK INSTANCE INFO'; + break; + + case 15: + param[0] = _makeList(entry[1], g_chr_classes, '[class=$1]'); + break; + + case 16: + param[0] = _makeList(entry[1], g_chr_races, '[race=$1]'); + break; + + case 20: + if (entry[1] == 0) + param[0] = $WH.sprintf('[span class=icon-$1]$2[/span]', g_file_genders[0], LANG.male); + else if (entry[1] == 1) + param[0] = $WH.sprintf('[span class=icon-$1]$2[/span]', g_file_genders[1], LANG.female); + else + param[0] = g_npc_types[10]; // not specified + break; + + case 21: + var states = {}; + for (let i in g_unit_states) + states[i * 1 + 1] = g_unit_states[i]; + + param[0] = _makeList(entry[1], states, '$2'); + break; + + case 22: + if (entry[2]) + param[0] = '[zone=' + entry[2] + ']'; + else + param[0] = g_zone_categories[entry[1]] ?? 'UNK ZONE'; + break; + + case 24: + param[0] = g_npc_types[entry[1]] ?? 'UNK NPC TYPE'; + break; + + case 26: + var idx = 0, buff = []; + while (entry[1] >= (1 << idx)) { + if (!(entry[1] & (1 << idx++))) + continue; + + buff.push(idx); + } + param[0] = buff ? buff.join(LANG.comma) : ''; + break; + + case 27: + case 37: + case 38: + param[1] = g_operators[entry[2]]; + break; + + case 31: + if (entry[2] && entry[1] == 3) + param[0] = '[npc=' + entry[2] + ']'; + else if (entry[2] && entry[1] == 5) + param[0] = '[object=' + entry[2] + ']'; + else + param[0] = g_world_object_types[entry[1]] ?? 'UNK TYPEID'; + break; + + case 32: + var objectTypes = {}; + for (let i in g_world_object_types) + objectTypes[i * 1 + 1] = g_world_object_types[i]; + + param[0] = _makeList(entry[1], objectTypes, '$2'); + break; + + case 33: + param[0] = targets[entry[1]]; + param[1] = g_relation_types[entry[2]] ?? 'UNK RELATION'; + param[2] = targets[target]; + break; + + case 34: + param[0] = targets[entry[1]]; + + var standings = {}; + for (let i in g_reputation_standings) + standings[i * 1 + 1] = g_reputation_standings[i]; + param[1] = _makeList(entry[2], standings, '$2'); + break; + + case 35: + param[0] = targets[entry[1]]; + param[2] = g_operators[entry[3]]; + break; + + case 42: + if (!entry[1]) + param[0] = g_stand_states[entry[2]] ?? 'UNK STAND_STATE'; + else if (entry[1] == 1) + param[0] = g_stand_states[entry[2] ? 1 : 0]; + else + param[0] = ''; + break; + + case 47: + var quest_states = {}; + for (let i in g_quest_states) + quest_states[i * 1 + 1] = g_quest_states[i]; + + param[1] = _makeList(entry[2], quest_states, '$2'); + break; + } + + str = g_conditions[strIdx]; + + // fill in params + str = $WH.sprintfa(str, param[0], param[1], param[2]); + + // resolve NegativeCondition + str = str.replace(/\$N([^:]*):([^;]*);/g, '$' + (negate > 0 ? 2 : 1)); + + // resolve vars + return str.replace(/\$C(\d+)([^:]*):([^;]*);/g, (_, i, y, n) => (i > 0 ? y : n)); + } + + function _createTab() + { + var buff = ''; + + // tabs for conditionsTypes + for (g in _conditions) + { + if (!g_condition_sources[g]) + continue; + + let k = 0; + for (h in _conditions[g]) + { + var srcGroup, srcEntry, srcId, target, + targets, desc, + nGroups = Object.keys(_conditions[g][h]).length, + curGroup = 1; + + [srcGroup, srcEntry, srcId, target] = h.split(':').map((x) => parseInt(x)); + [targets, desc] = g_condition_sources[g]; + + // resolve targeting + let src = desc.replace(/\$T([^:]*):([^;]*);/, (_, t1, t2) => (target ? t2 : t1).replace('%', targets[target])); + let rand = $WH.rs(); + + buff += '[h3][toggler' + (k ? '=hidden' : '') + ' id=' + rand + ']' + $WH.sprintfa(src, srcGroup, srcEntry, srcId) + '[/toggler][/h3][div' + (k++ ? '=hidden' : '') + ' id=' + rand + ']'; + + if (nGroups > 1) + { + buff += LANG.note_condition_group + '[br][br]'; + buff += '[table class=grid]'; + } + + // table for elseGroups + for (i in _conditions[g][h]) + { + var group = _conditions[g][h][i], + nEntries = Object.keys(_conditions[g][h][i]).length; + + if (nGroups <= 1 && nEntries > 1) + buff += '[div style="padding-left:15px"]' + LANG.note_condition + '[/div]'; + if (nGroups > 1) + buff += '[tr][td width=70px valign=middle align=center]' + LANG.group + ' ' + (curGroup++) + LANG.colon + '[/td][td]'; + + // individual conditions + buff += '[ol]'; + for (j in group) + buff += '[li]' + _parseEntry(group[j], targets, target) + '[/li]'; + buff += '[/ol]'; + + if (nGroups > 1) + buff += '[/td][/tr]'; + } + + if (nGroups > 1) + buff += '[/tr][/table]'; + + buff += '[/div]'; + } + } + + return buff; + } + + function _createCell() + { + var rows = []; + + // tabs for conditionsTypes + for (let g in _conditions) + { + if (!g_condition_sources[g]) + continue; + + for (let h in _conditions[g]) + { + var target, targets, + + [, , , target] = h.split(':').map((x) => parseInt(x)); + [targets, ] = g_condition_sources[g]; + + let nElseGroups = Object.keys(_conditions[g][h]).length + + // table for elseGroups + for (let i in _conditions[g][h]) + { + let subGroup = [], + group = _conditions[g][h][i], + nEntries = Object.keys(_conditions[g][h][i]).length + buff = ''; + + if (nElseGroups > 1) + { + let rand = $WH.rs(); + buff += '[toggler' + (i > 0 ? '=hidden' : '') + ' id=cell-' + rand + ']' + (i > 0 ? LANG.cnd_or : LANG.cnd_either) + '[/toggler][div' + (i > 0 ? '=hidden' : '') + ' id=cell-' + rand + ']'; + } + + // individual conditions + for (let j in group) + subGroup.push(_parseEntry(group[j], targets, target)); + + for (j in subGroup) + { + if (nEntries > 1 && j > 0 && j == subGroup.length - 1) + buff += LANG.and + '[br]'; + else if (nEntries > 1 && j > 0) + buff += ',[br]'; + + buff += subGroup[j]; + } + + if (nElseGroups > 1) + buff += '[/div]'; + + rows.push(buff); + } + } + } + + return rows.length > 1 ? rows.join('[br]') : rows[0]; + } + +} +/* end custom */ diff --git a/setup/tools/filegen/templates/global.js/contacttool.js b/setup/tools/filegen/templates/global.js/contacttool.js new file mode 100644 index 00000000..516bc733 --- /dev/null +++ b/setup/tools/filegen/templates/global.js/contacttool.js @@ -0,0 +1,550 @@ +var ContactTool = new function() +{ + this.general = 0; + this.comment = 1; + this.post = 2; + this.screenshot = 3; + this.character = 4; + this.video = 5; + this.guide = 6; + + var _dialog; + + var contexts = { + 0: [ // general + [1, true], // General feedback + [2, true], // Bug report + [8, true], // Article misinformation + [3, true], // Typo/mistranslation + [4, true], // Advertise with us + [5, true], // Partnership opportunities + [6, true], // Press inquiry + [7, true] // Other + ], + 1: [ // comment + [15, function(post) { return ((post.roles & U_GROUP_MODERATOR) == 0); }], // Advertising + [16, true], // Inaccurate + [17, true], // Out of date + [18, function(post) { return ((post.roles & U_GROUP_MODERATOR) == 0); }], // Spam + [19, function(post) { return ((post.roles & U_GROUP_MODERATOR) == 0); }], // Vulgar/inappropriate + [20, function(post) { return ((post.roles & U_GROUP_MODERATOR) == 0); }] // Other + ], + 2: [ // forum post + [30, function(post) { return (g_users && g_users[post.user] && (g_users[post.user].roles & U_GROUP_MODERATOR) == 0); }], // Advertising + [37, function(post) { return (g_users && g_users[post.user] && (g_users[post.user].roles & U_GROUP_MODERATOR) == 0 && (post.roles & U_GROUP_MODERATOR) == 0 && g_users[post.user].avatar == 2); }], // Avatar + [31, true], // Inaccurate + [32, true], // Out of date + [33, function(post) { return (g_users && g_users[post.user] && (g_users[post.user].roles & U_GROUP_MODERATOR) == 0); }], // Spam + [34, function(post) { return (g_users && g_users[post.user] && (g_users[post.user].roles & U_GROUP_MODERATOR) == 0 && post.op && !post.sticky); }], // Sticky request + [35, function(post) { return (g_users && g_users[post.user] && (g_users[post.user].roles & U_GROUP_MODERATOR) == 0); }], // Vulgar/inappropriate + [36, function(post) { return (g_users && g_users[post.user] && (g_users[post.user].roles & U_GROUP_MODERATOR) == 0);}] // Other + ], + 3: [ // screenshot + [45, true], // Inaccurate, + [46, true], // Out of date, + [47, function(screen) { return (g_users && g_users[screen.user] && (g_users[screen.user].roles & U_GROUP_MODERATOR) == 0); }], // Vulgar/inappropriate + [48, function(screen) { return (g_users && g_users[screen.user] && (g_users[screen.user].roles & U_GROUP_MODERATOR) == 0); }] // Other + ], + 4: [ // character + [60, true], // Inaccurate completion data + [61, true] // Other + ], + 5: [ // video + [45, true], // Inaccurate, + [46, true], // Out of date, + [47, function(video) { return (g_users && g_users[video.user] && (g_users[video.user].roles & U_GROUP_MODERATOR) == 0); }], // Vulgar/inappropriate + [48, function(video) { return (g_users && g_users[video.user] && (g_users[video.user].roles & U_GROUP_MODERATOR) == 0); }] // Other + ], + 6: [ // Guide + [45, true], // Inaccurate, + [46, true], // Out of date, + [48, true] // Other + ] + }; + + var errors = { + 1: LANG.ct_resp_error1, + 2: LANG.ct_resp_error2, + 3: LANG.ct_resp_error3, + 7: LANG.ct_resp_error7 + }; + + var oldHash = null; + + this.displayError = function(field, message) + { + alert(message); + } + + this.onShow = function() + { + if (location.hash && location.hash != '#contact') + oldHash = location.hash; + if (this.data.mode == 0) + location.replace('#contact'); + } + + this.onHide = function() + { + if (oldHash && (oldHash.indexOf('screenshots:') == -1 || oldHash.indexOf('videos:') == -1)) + location.replace(oldHash); + else + location.replace('#.'); + } + + this.onSubmit = function(data, button, form) + { + if (data.submitting) + return false; + + for (var i = 0; i < form.elements.length; ++i) + form.elements[i].disabled = true; + + var params = [ + 'contact=1', + 'mode=' + $WH.urlencode(data.mode), + 'reason=' + $WH.urlencode(data.reason), + 'desc=' + $WH.urlencode(data.description), + 'ua=' + $WH.urlencode(navigator.userAgent), + 'appname=' + $WH.urlencode(navigator.appName), + 'page=' + $WH.urlencode(data.currenturl) + ]; + + if (data.mode == 0) // contact us + { + if (data.relatedurl) + params.push('relatedurl=' + $WH.urlencode(data.relatedurl)); + if (data.email) + params.push('email=' + $WH.urlencode(data.email)); + } + else if (data.mode == 1) // comment + params.push('id=' + $WH.urlencode(data.comment.id)); + else if (data.mode == 2) // forum post + params.push('id=' + $WH.urlencode(data.post.id)); + else if (data.mode == 3) // screenshot + params.push('id=' + $WH.urlencode(data.screenshot.id)); + else if (data.mode == 4) // character + params.push('id=' + $WH.urlencode(data.profile.source)); + else if (data.mode == 5) // video + params.push('id=' + $WH.urlencode(data.video.id)); + else if (data.mode == 6) // guide + params.push('id=' + $WH.urlencode(data.guide.id)); + + data.submitting = true; + var url = '?contactus'; + new Ajax(url, { + method: 'POST', + params: params.join('&'), + onSuccess: function(xhr, opt) { + var resp = xhr.responseText; + if (resp == 0) + { + if (g_user.name) + alert($WH.sprintf(LANG.ct_dialog_thanks_user, g_user.name)); + else + alert(LANG.ct_dialog_thanks); + + Lightbox.hide(); + } + else + { + if (errors[resp]) + alert(errors[resp]); + else + alert('Error: ' + resp); + } + }, + onFailure: function(xhr, opt) { + alert('Failure submitting contact request: ' + xhr.statusText); + }, + onComplete: function(xhr, opt) { + for (var i = 0; i < form.elements.length; ++i) + form.elements[i].disabled = false; + + data.submitting = false; + } + }); + return false; + } + + this.show = function(opt) + { + if (!opt) + opt = {}; + + var data = { mode: 0 }; + $WH.cO(data, opt); + data.reasons = contexts[data.mode]; + if (location.href.indexOf('#contact') != -1) + data.currenturl = location.href.substr(0, location.href.indexOf('#contact')); + else + data.currenturl = location.href; + + var form = 'contactus'; + if (data.mode != 0) + form = 'reportform'; + + if (!_dialog) + { + this.init(); + } + + _dialog.show(form, { + data: data, + onShow: this.onShow, + onHide: this.onHide, + onSubmit: this.onSubmit + }) + } + + this.checkPound = function() + { + if (location.hash && location.hash == '#contact') + { + ContactTool.show(); + } + } + + var dialog_contacttitle = LANG.ct_dialog_contactwowhead; + + this.init = function() + { + _dialog = new Dialog(); + + Dialog.templates.contactus = { + title: dialog_contacttitle, + width: 550, + buttons: [['okay', LANG.ok], ['cancel', LANG.cancel]], + + fields: [ + { + id: 'reason', + type: 'select', + label: LANG.ct_dialog_reason, + required: 1, + options: [], + compute: function(field, value, form, td) + { + $WH.ee(field); + + for (var i = 0; i < this.data.reasons.length; ++i) + { + var id = this.data.reasons[i][0]; + var check = this.data.reasons[i][1]; + var valid = false; + if (typeof check == 'function') + valid = check(this.extra); + else + valid = check; + + if (!valid) + continue; + + var o = $WH.ce('option'); + o.value = id; + if (value && value == id) + o.selected = true; + + $WH.ae(o, $WH.ct(g_contact_reasons[id])); + $WH.ae(field, o); + } + + field.onchange = function() + { + if (this.value == 1 || this.value == 2 || this.value == 3) + { + form.currenturl.parentNode.parentNode.style.display = ''; + form.relatedurl.parentNode.parentNode.style.display = ''; + } + else + { + form.currenturl.parentNode.parentNode.style.display = 'none'; + form.relatedurl.parentNode.parentNode.style.display = 'none'; + } + }.bind(field); + + td.style.width = '98%'; + }, + validate: function(newValue, data, form) + { + var error = ''; + if (!newValue || newValue.length == 0) + error = LANG.ct_dialog_error_reason; + + if (error == '') + return true; + + ContactTool.displayError(form.reason, error); + form.reason.focus(); + return false; + } + }, + { + id: 'currenturl', + type: 'text', + disabled: true, + label: LANG.ct_dialog_currenturl, + size: 40 + }, + { + id: 'relatedurl', + type: 'text', + label: LANG.ct_dialog_relatedurl, + caption: LANG.ct_dialog_optional, + size: 40, + validate: function(newValue, data, form) + { + var error = ''; + var urlRe = /^(http(s?)\:\/\/|\/)?([\w]+:\w+@)?([a-zA-Z]{1}([\w\-]+\.)+([\w]{2,5}))(:[\d]{1,5})?((\/?\w+\/)+|\/?)(\w+\.[\w]{3,4})?((\?\w+=\w+)?(&\w+=\w+)*)?/; + newValue = newValue.trim(); + if (newValue.length >= 250) + error = LANG.ct_dialog_error_relatedurl; + else if (newValue.length > 0 && !urlRe.test(newValue)) + error = LANG.ct_dialog_error_invalidurl; + + if (error == '') + return true; + + ContactTool.displayError(form.relatedurl, error); + form.relatedurl.focus(); + return false; + } + }, + { + id: 'email', + type: 'text', + label: LANG.ct_dialog_email, + caption: LANG.ct_dialog_email_caption, + compute: function(field, value, form, td, tr) + { + if (g_user.email) + { + this.data.email = g_user.email; + tr.style.display = 'none'; + } + else + { + var func = function() + { + $('#contact-emailwarn').css('display', g_isEmailValid($(form.email).val()) ? 'none' : ''); + Lightbox.reveal(); + }; + + $(field).keyup(func).blur(func); + } + }, + validate: function(newValue, data, form) + { + var error = ''; + newValue = newValue.trim(); + if (newValue.length >= 100) + error = LANG.ct_dialog_error_emaillen; + else if (newValue.length > 0 && !g_isEmailValid(newValue)) + error = LANG.ct_dialog_error_email; + + if (error == '') + return true; + + ContactTool.displayError(form.email, error); + form.email.focus(); + return false; + } + }, + { + id: 'description', + type: 'textarea', + caption: LANG.ct_dialog_desc_caption, + width: '98%', + required: 1, + size: [10, 30], + validate: function(newValue, data, form) + { + var error = ''; + newValue = newValue.trim(); + if (newValue.length == 0 || newValue.length > 10000) + error = LANG.ct_dialog_error_desc; + + if (error == '') + return true; + + ContactTool.displayError(form.description, error); + form.description.focus(); + return false; + } + }, + { + id: 'noemailwarning', + type: 'caption', + compute: function(field, value, form, td) + { + $(td).html('').css('white-space', 'normal').css('padding', '0 4px'); + } + } + ], + + onInit: function(form) + { + + }, + + onShow: function(form) + { + if (this.data.focus && form[this.data.focus]) + setTimeout(g_setCaretPosition.bind(null, form[this.data.focus], form[this.data.focus].value.length), 100); + else if (form['reason'] && !form.reason.value) + setTimeout($WH.bindfunc(form.reason.focus, form.reason), 10); + else if (form['relatedurl'] && !form.relatedurl.value) + setTimeout($WH.bindfunc(form.relatedurl.focus, form.relatedurl), 10); + else if (form['email'] && !form.email.value) + setTimeout($WH.bindfunc(form.email.focus, form.email), 10); + else if (form['description'] && !form.description.value) + setTimeout($WH.bindfunc(form.description.focus, form.description), 10); + + setTimeout(Lightbox.reveal, 250); + } + } + + Dialog.templates.reportform = { + title: LANG.ct_dialog_report, + width: 550, + // height: 360, + buttons: [['okay', LANG.ok], ['cancel', LANG.cancel]], + fields: [ + { + id: 'reason', + type: 'select', + label: LANG.ct_dialog_reason, + options: [], + compute: function(field, value, form, td) + { + switch (this.data.mode) + { + case 1: // comment + form.firstChild.innerHTML = $WH.sprintf(LANG.ct_dialog_reportcomment, '' + this.data.comment.user + ''); + break; + case 2: // forum post + var rep = '' + this.data.post.user + ''; + if (this.data.post.op) + form.firstChild.innerHTML = $WH.sprintf(LANG.ct_dialog_reporttopic, rep); + else + form.firstChild.innerHTML = $WH.sprintf(LANG.ct_dialog_reportpost, rep); + break; + case 3: // screenshot + form.firstChild.innerHTML = $WH.sprintf(LANG.ct_dialog_reportscreen, '' + this.data.screenshot.user + ''); + break; + case 4: // character + $WH.ee(form.firstChild); + $WH.ae(form.firstChild, $WH.ct(LANG.ct_dialog_reportchar)); + break; + case 5: // video + form.firstChild.innerHTML = $WH.sprintf(LANG.ct_dialog_reportvideo, '' + this.data.video.user + ''); + break; + case 6: // guide + form.firstChild.innerHTML = 'Report guide'; + break; + } + form.firstChild.setAttribute('style', ''); + + $WH.ee(field); + + var extra; + if (this.data.mode == 1) + extra = this.data.comment; + else if (this.data.mode == 2) + extra = this.data.post; + else if (this.data.mode == 3) + extra = this.data.screenshot; + else if (this.data.mode == 4) + extra = this.data.profile; + else if (this.data.mode == 5) + extra = this.data.video; + else if (this.data.mode == 6) + extra = this.data.guide; + + $WH.ae(field, $WH.ce('option', { selected: (!value), value: -1 })); + + for (var i = 0; i < this.data.reasons.length; ++i) + { + var id = this.data.reasons[i][0]; + var check = this.data.reasons[i][1]; + var valid = false; + if (typeof check == 'function') + valid = check(extra); + else + valid = check; + + if (!valid) + continue; + + var o = $WH.ce('option'); + o.value = id; + if (value && value == id) + o.selected = true; + + $WH.ae(o, $WH.ct(g_contact_reasons[id])); + $WH.ae(field, o); + } + + td.style.width = '98%'; + }, + validate: function(newValue, data, form) + { + var error = ''; + if (!newValue || newValue == -1 || newValue.length == 0) + error = LANG.ct_dialog_error_reason; + + if (error == '') + return true; + + ContactTool.displayError(form.reason, error); + form.reason.focus(); + return false; + } + }, + { + id: 'description', + type: 'textarea', + caption: LANG.ct_dialog_desc_caption, + width: '98%', + required: 1, + size: [10, 30], + validate: function(newValue, data, form) + { + var error = ''; + newValue = newValue.trim(); + if (newValue.length == 0 || newValue.length > 10000) + error = LANG.ct_dialog_error_desc; + + if (error == '') + return true; + + ContactTool.displayError(form.description, error); + form.description.focus(); + return false; + } + } + ], + + onInit: function(form) + { + + }, + + onShow: function(form) + { + /* Work-around for IE7 */ + var reason = $(form).find("*[name=reason]")[0]; + var description = $(form).find("*[name=description]")[0]; + + if (this.data.focus && form[this.data.focus]) + setTimeout(g_setCaretPosition.bind(null, form[this.data.focus], form[this.data.focus].value.length), 100); + else if (!reason.value) + setTimeout($WH.bindfunc(reason.focus, reason), 10); + else if (!description.value) + setTimeout($WH.bindfunc(description.focus, description), 10); + } + } + } + + $(document).ready(this.checkPound); +}; diff --git a/setup/tools/filegen/templates/global.js/cookies.js b/setup/tools/filegen/templates/global.js/cookies.js new file mode 100644 index 00000000..3cce1d3e --- /dev/null +++ b/setup/tools/filegen/templates/global.js/cookies.js @@ -0,0 +1,37 @@ +// TODO: Create a "Cookies" object + +function g_cookiesEnabled() +{ + document.cookie = 'enabledTest'; + return (document.cookie.indexOf("enabledTest") != -1) ? true : false; +} + +function g_getWowheadCookie(name) +{ + if (g_user.id > 0) + { + return g_user.cookies[name]; // no point checking if it exists, as undefined tests as false anyways + } + else + { + return $WH.gc(name); // plus gc does the same thing.. + } +} + +function g_setWowheadCookie(name, data, browser) +{ + var temp = name.substr(0, 5) == 'temp_'; + if (!browser && g_user.id > 0 && !temp) { + new Ajax('?cookie=' + name + '&' + name + '=' + $WH.urlencode(data), { + method: 'get', + onSuccess: function(xhr) { + if (xhr.responseText == 0) + g_user.cookies[name] = data; + } + }); + } + else if (browser || g_user.id == 0) + { + $WH.sc(name, 14, data, null, location.hostname); + } +} diff --git a/setup/tools/filegen/templates/global.js/dialog.js b/setup/tools/filegen/templates/global.js/dialog.js new file mode 100644 index 00000000..e46fa3a4 --- /dev/null +++ b/setup/tools/filegen/templates/global.js/dialog.js @@ -0,0 +1,568 @@ +var Dialog = function() +{ +var + _self = this, + _template, + _onSubmit = null, + _templateName, + + _funcs = {}, + _data, + + _inited = false, + _form = $WH.ce('form'), + _elements = {}; + + _form.onsubmit = function() { + _processForm(); + return false + }; + + this.show = function(template, opt) + { + if (template) + { + _templateName = template; + _template = Dialog.templates[_templateName]; + + _self.template = _template; + } + else + return; + + if (_template.onInit && !_inited) + (_template.onInit.bind(_self, _form, opt))(); + + if (opt.onBeforeShow) + _funcs.onBeforeShow = opt.onBeforeShow.bind(_self, _form); + + if (_template.onBeforeShow) + _template.onBeforeShow = _template.onBeforeShow.bind(_self, _form); + + if (opt.onShow) + _funcs.onShow = opt.onShow.bind(_self, _form); + + if (_template.onShow) + _template.onShow = _template.onShow.bind(_self, _form); + + if (opt.onHide) + _funcs.onHide = opt.onHide.bind(_self, _form); + + if (_template.onHide) + _template.onHide = _template.onHide.bind(_self, _form); + + if (opt.onSubmit) + _funcs.onSubmit = opt.onSubmit; + + if (_template.onSubmit) + _onSubmit = _template.onSubmit.bind(_self, _form); + + if (opt.data) + { + _inited = false; + _data = {}; + $WH.cO(_data, opt.data); + } + + _self.data = _data; + + Lightbox.show('dialog-' + _templateName, { + onShow: _onShow, + onHide: _onHide + }); + } + + this.getValue = function(id) + { + return _getValue(id); + } + + this.setValue = function(id, value) + { + _setValue(id, value); + } + + this.getSelectedValue = function(id) + { + return _getSelectedValue(id); + } + + this.getCheckedValue = function(id) + { + return _getCheckedValue(id); + } + + function _onShow(dest, first) + { + if (first || !_inited) + _initForm(dest); + + if (_template.onBeforeShow) + _template.onBeforeShow(); + + if (_funcs.onBeforeShow) + _funcs.onBeforeShow(); + + Lightbox.setSize(_template.width, _template.height); + dest.className = 'dialog'; + + _updateForm(); + + if (_template.onShow) + _template.onShow(); + + if (_funcs.onShow) + _funcs.onShow(); + } + + function _initForm(dest) + { + $WH.ee(dest); + $WH.ee(_form); + + var container = $WH.ce('div'); + container.className = 'text'; + $WH.ae(dest, container); + + $WH.ae(container, _form); + + if (_template.title) + { + var h = $WH.ce('h1'); + $WH.ae(h, $WH.ct(_template.title)); + $WH.ae(_form, h); + } + + var t = $WH.ce('table'), + tb = $WH.ce('tbody'), + mergeCell = false; + + $WH.ae(t, tb); + $WH.ae(_form, t); + + for (var i = 0, len = _template.fields.length; i < len; ++i) + { + var + field = _template.fields[i], + element; + + if (!mergeCell) + { + tr = $WH.ce('tr'); + th = $WH.ce('th'); + td = $WH.ce('td'); + } + + field.__tr = tr; + + if (_data[field.id] == null) + _data[field.id] = (field.value ? field.value : ''); + + var options; + if (field.options) + { + options = []; + + if (field.optorder) + $WH.cO(options, field.optorder); + else + { + for (var j in field.options) + options.push(j); + } + + if (field.sort) + options.sort(function(a, b) { return field.sort * $WH.strcmp(field.options[a], field.options[b]); }); + } + + switch (field.type) + { + case 'caption': + + th.colSpan = 2; + th.style.textAlign = 'left'; + th.style.padding = 0; + + if (field.compute) + (field.compute.bind(_self, null, _data[field.id], _form, th, tr))(); + else if (field.label) + $WH.ae(th, $WH.ct(field.label)); + + $WH.ae(tr, th); + $WH.ae(tb, tr); + + continue; + break; + + case 'textarea': + + var f = element = $WH.ce('textarea'); + + f.name = field.id; + + if (field.disabled) + f.disabled = true; + + f.rows = field.size[0]; + f.cols = field.size[1]; + + td.colSpan = 2; + + if (field.label) + { + th.colSpan = 2; + th.style.textAlign = 'left'; + th.style.padding = 0; + td.style.padding = 0; + + $WH.ae(th, $WH.ct(field.label)); + $WH.ae(tr, th); + $WH.ae(tb, tr); + + tr = $WH.ce('tr'); + } + + $WH.ae(td, f); + + break; + + case 'select': + + var f = element = $WH.ce('select'); + + f.name = field.id; + + if (field.size) + f.size = field.size; + + if (field.disabled) + f.disabled = true; + + if (field.multiple) + f.multiple = true; + + for (var j = 0, len2 = options.length; j < len2; ++j) + { + var o = $WH.ce('option'); + + o.value = options[j]; + + $WH.ae(o, $WH.ct(field.options[options[j]])); + $WH.ae(f, o) + } + + $WH.ae(td, f); + + break; + + case 'dynamic': + + td.colSpan = 2; + td.style.textAlign = 'left'; + td.style.padding = 0; + + if (field.compute) + (field.compute.bind(_self, null, _data[field.id], _form, td, tr))(); + + $WH.ae(tr, td); + $WH.ae(tb, tr); + + element = td; + + break; + + case 'checkbox': + case 'radio': + + var k = 0; + element = []; + for (var j = 0, len2 = options.length; j < len2; ++j) + { + var + s = $WH.ce('span'), + f, + l, + uniqueId = 'sdfler46' + field.id + '-' + options[j]; + + if (j > 0 && !field.noInputBr) + $WH.ae(td, $WH.ce('br')); + + l = $WH.ce('label'); + l.setAttribute('for', uniqueId); + l.onmousedown = $WH.rf; + + f = $WH.ce('input', { name: field.id, value: options[j], id: uniqueId }); + f.setAttribute('type', field.type); + + if (field.disabled) + f.disabled = true; + + if (field.submitOnDblClick) + l.ondblclick = f.ondblclick = function(e) { _processForm(); }; + + if (field.compute) + (field.compute.bind(_self, f, _data[field.id], _form, td, tr))(); + + $WH.ae(l, f); + $WH.ae(l, $WH.ct(field.options[options[j]])); + $WH.ae(td, l); + + element.push(f); + } + + break; + + default: // Textbox + + var f = element = $WH.ce('input'); + + f.name = field.id; + + if (field.size) + f.size = field.size; + + if (field.disabled) + f.disabled = true; + + if (field.submitOnEnter) + { + f.onkeypress = function(e) { + e = $WH.$E(e); + if (e.keyCode == 13) + _processForm(); + }; + } + + f.setAttribute('type', field.type); + + $WH.ae(td, f); + + break; + } + + if (field.label) + { + if (field.type == 'textarea') + { + if (field.labelAlign) + td.style.textAlign = field.labelAlign; + + td.colSpan = 2; + } + else + { + if (field.labelAlign) + th.style.textAlign = field.labelAlign; + + $WH.ae(th, $WH.ct(field.label)); + $WH.ae(tr, th); + } + } + + if (field.placeholder) + f.placeholder = field.placeholder; + + if (field.type != 'checkbox' && field.type != 'radio') + { + if (field.width) + f.style.width = field.width; + + if (field.compute && field.type != 'caption' && field.type != 'dynamic') + (field.compute.bind(_self, f, _data[field.id], _form, td, tr))(); + } + + if (field.caption) + { + var s = $WH.ce('small'); + if (field.type != 'textarea') + s.style.paddingLeft = '2px'; + s.className = 'q0'; // commented in 5.0? + $WH.ae(s, $WH.ct(field.caption)); + $WH.ae(td, s); + } + + $WH.ae(tr, td); + $WH.ae(tb, tr); + + mergeCell = field.mergeCell; + + _elements[field.id] = element; + } + + for (var i = _template.buttons.length; i > 0; --i) + { + var + button = _template.buttons[i - 1], + a = $WH.ce('a'); + + a.onclick = _processForm.bind(a, button[0]); + a.className = 'dialog-' + button[0]; + a.href = 'javascript:;'; + $WH.ae(a, $WH.ct(button[1])); + $WH.ae(dest, a); + } + + var _ = $WH.ce('div'); + _.className = 'clear'; + $WH.ae(dest, _); + + _inited = true; + } + + function _updateForm() + { + for (var i = 0, len = _template.fields.length; i < len; ++i) + { + var + field = _template.fields[i], + f = _elements[field.id]; + + switch (field.type) + { + case 'caption': // Do nothing + break; + + case 'select': + for (var j = 0, len2 = f.options.length; j < len2; j++) + f.options[j].selected = (f.options[j].value == _data[field.id] || $WH.in_array(_data[field.id], f.options[j].value) != -1); + break; + + case 'checkbox': + case 'radio': + for (var j = 0, len2 = f.length; j < len2; j++) + f[j].checked = (f[j].value == _data[field.id] || $WH.in_array(_data[field.id], f[j].value) != -1); + break; + + default: + f.value = _data[field.id]; + break; + } + + if (field.update) + (field.update.bind(_self, null, _data[field.id], _form, f))(); + } + } + + function _onHide() + { + if (_template.onHide) + _template.onHide(); + + if (_funcs.onHide) + _funcs.onHide(); + } + + function _processForm(button) + { + // if (button == 'x') // aowow - button naming differs + if (button == 'cancel') // Special case + return Lightbox.hide(); + + for (var i = 0, len = _template.fields.length; i < len; ++i) + { + var + field = _template.fields[i], + newValue; + + switch (field.type) + { + case 'caption': // Do nothing + continue; + + case 'select': + newValue = _getSelectedValue(field.id); + break; + + case 'checkbox': + case 'radio': + newValue = _getCheckedValue(field.id); + break; + + case 'dynamic': + if (field.getValue) + { + newValue = field.getValue(field, _data, _form); + break; + } + default: + newValue = _getValue(field.id); + break; + } + + if (field.validate) + { + if (!field.validate(newValue, _data, _form)) + return; + } + + if (newValue && typeof newValue == 'string') + newValue = $WH.trim(newValue); + + _data[field.id] = newValue; + } + + _submitData(button); + } + + function _submitData(button) + { + var ret; + + if (_onSubmit) + ret = _onSubmit(_data, button, _form); + + if (_funcs.onSubmit) + ret = _funcs.onSubmit(_data, button, _form); + + if (ret === undefined || ret) + Lightbox.hide(); + + return false; + } + + function _getValue(id) + { + return _elements[id].value; + } + + function _setValue(id, value) + { + _elements[id].value = value; + } + + function _getSelectedValue(id) + { + var + result = [], + f = _elements[id]; + + for (var i = 0, len = f.options.length; i < len; i++) + { + if (f.options[i].selected) + result.push(parseInt(f.options[i].value) == f.options[i].value ? parseInt(f.options[i].value) : f.options[i].value); + } + + if (result.length == 1) + result = result[0]; + + return result; + } + + function _getCheckedValue(id) + { + var + result = [], + f = _elements[id]; + + for (var i = 0, len = f.length; i < len; i++) + { + if (f[i].checked) + result.push(parseInt(f[i].value) == f[i].value ? parseInt(f[i].value) : f[i].value); + } + + return result; + } +}; + +Dialog.templates = {}; +Dialog.extraFields = {}; diff --git a/setup/tools/filegen/templates/global.js/dom_manipulation.js b/setup/tools/filegen/templates/global.js/dom_manipulation.js new file mode 100644 index 00000000..787c45e9 --- /dev/null +++ b/setup/tools/filegen/templates/global.js/dom_manipulation.js @@ -0,0 +1,252 @@ +/* +Global functions related to DOM manipulation, events & forms that jQuery doesn't already provide +*/ + +function g_addCss(css) +{ + var style = $WH.ce('style'); + style.type = 'text/css'; + + if (style.styleSheet) // ie + style.styleSheet.cssText = css; + else + $WH.ae(style, $WH.ct(css)); + + var head = $WH.gE(document, 'head')[0]; + $WH.ae(head, style); +} + +function g_setTextNodes(n, text) +{ + if (n.nodeType == 3) + n.nodeValue = text; + else + { + for (var i = 0; i < n.childNodes.length; ++i) + g_setTextNodes(n.childNodes[i], text); + } +} + +function g_setInnerHtml(n, text, nodeType) +{ + if (n.nodeName.toLowerCase() == nodeType) + n.innerHTML = text; + else + { + for (var i = 0; i < n.childNodes.length; ++i) + g_setInnerHtml(n.childNodes[i], text, nodeType); + } +} + +function g_getFirstTextContent(node) +{ + for (var i = 0; i < node.childNodes.length; ++i) + { + if (node.childNodes[i].nodeName == '#text') + return node.childNodes[i].nodeValue; + + var ret = g_getFirstTextContent(node.childNodes[i]); + if (ret) + return ret; + } + + return false; +} + +function g_getTextContent(el) +{ + var txt = ''; + for (var i = 0; i < el.childNodes.length; ++i) + { + if (el.childNodes[i].nodeValue) + txt += el.childNodes[i].nodeValue; + else if (el.childNodes[i].nodeName == 'BR') + txt += '\n'; + + txt += g_getTextContent(el.childNodes[i]); + } + + return txt; +} + +function g_toggleDisplay(el) +{ + el = $(el); + el.toggle(); + if (el.is(':visible')) + return true; + + return false; +} + +function g_enableScroll(enabled) +{ + if (!enabled) + { + $WH.aE(document, 'mousewheel', g_enableScroll.F); + $WH.aE(window, 'DOMMouseScroll', g_enableScroll.F); + } + else + { + $WH.dE(document, 'mousewheel', g_enableScroll.F); + $WH.dE(window, 'DOMMouseScroll', g_enableScroll.F); + } +} + +g_enableScroll.F = function(e) +{ + if (e.stopPropagation) + e.stopPropagation(); + if (e.preventDefault) + e.preventDefault(); + + e.returnValue = false; + e.cancelBubble = true; + + return false; +}; + +// from http://blog.josh420.com/archives/2007/10/setting-cursor-position-in-a-textbox-or-textarea-with-javascript.aspx +function g_setCaretPosition(elem, caretPos) +{ + if (!elem) + return; + + if (elem.createTextRange) + { + var range = elem.createTextRange(); + range.move('character', caretPos); + range.select(); + } + else if (elem.selectionStart != undefined) + { + elem.focus(); + elem.setSelectionRange(caretPos, caretPos); + } + else + elem.focus(); +} + +function g_insertTag(where, tagOpen, tagClose, repFunc) +{ + var n = $WH.ge(where); + + n.focus(); + if (n.selectionStart != null) + { + var s = n.selectionStart, + e = n.selectionEnd, + sL = n.scrollLeft, + sT = n.scrollTop; + + var selectedText = n.value.substring(s, e); + if (typeof repFunc == 'function') + selectedText = repFunc(selectedText); + + n.value = n.value.substr(0, s) + tagOpen + selectedText + tagClose + n.value.substr(e); + n.selectionStart = n.selectionEnd = e + tagOpen.length; + + n.scrollLeft = sL; + n.scrollTop = sT; + } + else if (document.selection && document.selection.createRange) + { + var range = document.selection.createRange(); + + if (range.parentElement() != n) + return; + + var selectedText = range.text; + if (typeof repFunc == 'function') + selectedText = repFunc(selectedText); + + range.text = tagOpen + selectedText + tagClose; +/* + range.moveEnd("character", -tagClose.length); + range.moveStart("character", range.text.length); + + range.select(); +*/ + } + + if (n.onkeyup) + n.onkeyup(); +} + +function g_onAfterTyping(input, func, delay) +{ + var timerId; + var ldsgksdgnlk623 = function() + { + if (timerId) + { + clearTimeout(timerId); + timerId = null; + } + timerId = setTimeout(func, delay); + }; + input.onkeyup = ldsgksdgnlk623; +} + +function g_onClick(el, func) +{ + var firstEvent = 0; + + function rightClk(n) + { + if (firstEvent) + { + if (firstEvent != n) + return; + } + else + firstEvent = n; + + func(true); + } + + el.onclick = function(e) + { + e = $WH.$E(e); + + if (e._button == 2) // middle click + return true; + + return false; + } + + el.oncontextmenu = function() + { + rightClk(1); + + return false; + } + + el.onmouseup = function(e) + { + e = $WH.$E(e); + + if (e._button == 3 || e.shiftKey || e.ctrlKey) // Right/Shift/Ctrl + { + rightClk(2); + } + else if (e._button == 1) // Left + { + func(false); + } + + return false; + } +} + +function g_isLeftClick(e) +{ + e = $WH.$E(e); + return (e && e._button == 1); +} + +function g_preventEmptyFormSubmission() // Used on the homepage and in the top bar +{ + if (!$.trim(this.elements[0].value)) + return false; +} diff --git a/setup/tools/filegen/templates/global.js/favorites.js b/setup/tools/filegen/templates/global.js/favorites.js new file mode 100644 index 00000000..2470302f --- /dev/null +++ b/setup/tools/filegen/templates/global.js/favorites.js @@ -0,0 +1,262 @@ +var Favorites = new function() +{ + var _type = null; + var _typeId = null; + var _favIcon = null; + + this.pageInit = function(h1, type, typeId) + { + if (typeof h1 == 'string') + { + if (!document.querySelector) + return; + + h1 = document.querySelector(h1); + } + + if (!h1 || typeof type != 'number' || typeof typeId != 'number') + return; + + _type = type; + _typeId = typeId; + + createIcon(h1); + } + + function initFavIcon() + { + var h1 = typeof g_pageInfo == 'object' && typeof g_pageInfo.type == 'number' && typeof g_pageInfo.typeId == 'number' ? document.querySelector('#main-contents h1') : null; + if (!h1) { + if (document.readyState !== 'complete') + setTimeout(initFavIcon, 9); + + return; + } + + _type = g_pageInfo.type; + _typeId = g_pageInfo.typeId; + + createIcon(h1); + } + + this.hasFavorites = function() + { + return !!g_favorites.length + } + + this.getMenu = function() + { + var favMenu = []; + var nGroups = 0; + var nEntries = 0; + + for (var i = 0, favGroup; favGroup = g_favorites[i]; i++) + { + if (!favGroup.entities.length) + continue; + + nGroups++; + var subMenu = []; + for (var j = 0, favEntry; favEntry = favGroup.entities[j]; j++) + { + subMenu.push([favEntry[0], favEntry[1], '?' + g_types[favGroup.id] + '=' + favEntry[0]]); + nEntries++ + } + + Menu.sort(subMenu); + favMenu.push([favGroup.id, LANG.types[favGroup.id][2], , subMenu]) + } + + Menu.sort(favMenu); + + // display short favorites as 1-dim list + if ((nGroups == 1 && nEntries <= 45) || (nGroups == 2 && nGroups + nEntries <= 30) || (nGroups > 2 && nGroups + nEntries <= 15)) + { + var list = []; + + for (var i = 0; subMenu = favMenu[i]; i++) + { + list.push([, subMenu[MENU_IDX_NAME]]); + + for (var j = 0, subEntry; subEntry = subMenu[MENU_IDX_SUB][j]; j++) + { + var listEntry = [subEntry[MENU_IDX_ID], subEntry[MENU_IDX_NAME], subEntry[MENU_IDX_URL]]; + + if (subEntry[MENU_IDX_OPT]) + listEntry[MENU_IDX_OPT] = subEntry[MENU_IDX_OPT]; + + list.push(listEntry); + } + } + + favMenu = list; + } + + return favMenu; + } + + this.refreshMenu = function() + { + var menuRoot = $('#toplinks-favorites'); + if (!menuRoot.length) + return; + + var favMenu = Favorites.getMenu(); + if (!favMenu.length) { + menuRoot.hide(); + return; + } + + Menu.add(menuRoot, favMenu); + menuRoot.show(); + } + + function createIcon(heading) + { + _favIcon = $('', { + 'class': 'fav-star', + mouseout: $WH.Tooltip.hide + }).appendTo(heading); + + if (g_user.id) + { + _favIcon.addClass('fav-star' + (isFaved(_type, _typeId) ? '-1' : '-0')).click((function(type, typeId, name) { + toggleEntry(type, typeId, name); + updateIcon(type, typeId); + $WH.Tooltip.hide(); + }).bind(null, _type, _typeId, heading.textContent.trim().replace(/(.+)<.*/, '$1'))); + + _favIcon.mouseover(function(event) { + var tt = this.className.match(/\bfav-star-0\b/) ? LANG.addtofavorites : LANG.removefromfavorites; + $WH.Tooltip.show(this, tt, false, false, 'q2'); + }); + + } + else + { + _favIcon.addClass('fav-star-0').click(function() { + location.href = "?account=signin"; + $WH.Tooltip.hide(); + }).mouseover(function(event) { + $WH.Tooltip.show(this, LANG.favorites_login + '
' + LANG.clicktologin + ''); + }); + } + } + + function updateIcon(type, typeId) + { + if (_favIcon) + { + var rmv = 'fav-star-0'; + var add = 'fav-star-1'; + if (!isFaved(type, typeId)) + { + rmv = 'fav-star-1'; + add = 'fav-star-0'; + } + + _favIcon.removeClass(rmv).addClass(add); + } + } + + function isFaved(type, typeId) + { + var idx = getIndex(type); + if (idx == -1) + return false; + + for (var i = 0, j; j = g_favorites[idx].entities[i]; i++) + if (j[0] == typeId) + return true; + + return false; + } + + function toggleEntry(type, typeId, name) + { + if (isFaved(type, typeId)) + removeEntry(type, typeId); + else + addEntry(type, typeId, name); + } + + function addEntry(type, typeId, name) + { + var idx = getIndex(type, true); + if (idx == -1) + { + /* $WH. */ console.error("Invalid type when adding entity to favorites! Type was:", type); + return; + } + + for (var i = 0, j; j = g_favorites[idx].entities[i]; i++) + { + if (j[0] == typeId) + { + alert(LANG.favorites_duplicate.replace('%s', LANG.types[type][1])); + return; + } + } + + sendUpdate('add', type, typeId); + g_favorites[idx].entities.push([typeId, name]); + Favorites.refreshMenu(); + } + + function removeEntry(type, typeId) + { + var idx = getIndex(type); + if (idx == -1) + return; + + for (var i = 0, j; j = g_favorites[idx].entities[i]; i++) + { + if (j[0] == typeId) + { + sendUpdate('remove', type, typeId); + g_favorites[idx].entities.splice(i, 1); + if (!g_favorites[idx].entities.length) + g_favorites.splice(idx, 1); + + Favorites.refreshMenu(); + return; + } + } + } + + function getIndex(type, createNew) + { + if (!LANG.types[type]) + return -1; + + for (var i = 0, j; j = g_favorites[i]; i++) + if (j.id == type) + return i; + + if (!createNew) + return -1; + + g_favorites.push({ id: type, entities: [] }); + + g_favorites.sort(function(a, b) { return $WH.strcmp(LANG.types[a.id], LANG.types[b.id]) }); + + for (i = 0; j = g_favorites[i]; i++) + if (j.id == type) + return i; + + return -1; + } + + function sendUpdate(method, type, typeId) + { + var data = { + id: typeId, + // sessionKey: g_user.sessionKey + }; + data[method] = type; + $.post('?account=favorites', data); + } + + if (document.querySelector && $WH.localStorage.isSupported()) + initFavIcon(); +}; diff --git a/setup/tools/filegen/templates/global.js/guide.js b/setup/tools/filegen/templates/global.js/guide.js new file mode 100644 index 00000000..254abab5 --- /dev/null +++ b/setup/tools/filegen/templates/global.js/guide.js @@ -0,0 +1,368 @@ +var g_localTime = new Date(); + +/* This function is to get the stars for the vote control for the guides. */ + +function GetStars(stars, ratable, userRating, guideId) +{ + var STARS_MAX = 5; + var averageRating = stars; + + if (userRating) + stars = userRating; + + stars = Math.round(stars*2)/2; + var starsRounded = Math.round(stars); + var ret = $("").addClass('stars').addClass('max-' + STARS_MAX).addClass('stars-' + starsRounded); + + if (!g_user.id) + ratable = false; + + if (ratable) + ret.addClass('ratable'); + + if (userRating) + ret.addClass('rated'); + + /* This is kinda lame but oh well */ + var contents = ''; + + var wbr = '​'; + var tmp = stars; + for (var i = 1; i <= STARS_MAX; ++i) + { + if (tmp < 1 && tmp > 0) + contents += ''; + else + contents += ''; + --tmp; + + contents += '' + wbr + ''; + } + + for (var i = 1; i <= STARS_MAX; ++i) + contents += ''; + + contents += ''; + + ret.append(contents); + + if (ratable) + { + var starNumber = 0; + ret.find('i.clickable').each(function() { var starId = ++starNumber; $(this).click(function() { VoteGuide(guideId, averageRating, starId); }); }) + } + + if (userRating) + { + var clear = $("").addClass('clear').click(function() { VoteGuide(guideId, averageRating, 0); }); + ret.append(clear); + } + + if (stars >= 0) + ret.mouseover(function(event) {$WH.Tooltip.showAtCursor(event, 'Rating: ' + stars + ' / ' + STARS_MAX, 0, 0, 'q');}).mousemove(function(event) {$WH.Tooltip.cursorUpdate(event)}).mouseout(function() {$WH.Tooltip.hide()}); + + return ret; +} + +function VoteGuide(guideId, oldRating, newRating) +{ + // Update stars display + $('#guiderating').html(GetStars(oldRating, true, newRating, guideId)); + + // Vote + $.ajax({cache: false, url: '?guide=vote', type: 'POST', + error: function() { + $('#guiderating').html(GetStars(oldRating, true, 0, guideId)); + alert('Voting failed. Try again later.'); + }, + success: function(json) { + var data = eval('(' + json + ')'); + $('#guiderating-value').text(data.rating); + $('#guiderating-votes').text(GetN5(data.nvotes)); + }, + data: { id: guideId, rating: newRating } + }); +} + +/* g_enhanceTextarea and createOptionsMenuWidget are only ever used by the article/guide editor. Why are they in global.js? */ + +function g_enhanceTextarea (ta, opt) { + if (!(ta instanceof jQuery)) + ta = $(ta); + + if (ta.data("wh-enhanced") || ta.prop("tagName") != "TEXTAREA") + return; + + if (typeof opt != "object") + opt = {}; + + var canResize = (function(el) { + if (!el.dynamicResizeOption) + return true; + + if ($WH.localStorage.get("dynamic-textarea-resizing") === "true") + return true; + + if ($WH.localStorage.get("dynamic-textarea-resizing") === "false") + return false; + + return !el.hasOwnProperty("dynamicSizing") || el.dynamicSizing; + }).bind(null, opt); + + var height = ta.height() || 500; + var wrapper = $("
", { "class": "enhanced-textarea-wrapper" }).insertBefore(ta).append(ta); + + if (!opt.hasOwnProperty("color")) + wrapper.addClass("enhanced-textarea-dark"); + else if (opt.color) + wrapper.addClass("enhanced-textarea-" + opt.color); + + if (!opt.hasOwnProperty("dynamicSizing") || opt.dynamicSizing || opt.dynamicResizeOption) { + var expander = $("
", { "class": "enhanced-textarea-expander" }).prependTo(wrapper); + var dynamicResize = function(textarea, exactHeight, canResizeFn) { + if (!canResizeFn()) + return; + + // E.css("height", E.siblings(".enhanced-textarea-expander").html($WH.htmlentities(E.val()).replace(/\n/g, "
") + "
").height() + (D ? 14 : 34) + "px"); + textarea.css("height", textarea.siblings(".enhanced-textarea-expander").html($WH.htmlentities(textarea.val()) + "
").height() + (exactHeight ? 14 : 34) + "px"); + }; + + ta.bind("keydown keyup change", dynamicResize.bind(this, ta, opt.exactLineHeights, canResize)); + dynamicResize(ta, opt.exactLineHeights, canResize); + + var setWidth = function(el) { el.css("width", el.parent().width() + "px"); }; + + setWidth(expander); + setTimeout(setWidth.bind(null, expander), 1); + + if (!opt.dynamicResizeOption || (opt.dynamicResizeOption && canResize())) + wrapper.addClass("enhanced-textarea-dynamic-sizing"); + } + + if (!opt.hasOwnProperty("focusChanges") || opt.focusChanges) + wrapper.addClass("enhanced-textarea-focus-changes"); + + if (opt.markup) { + var _markupMenu = $("
", { "class": "enhanced-textarea-markup-wrapper" }).prependTo(wrapper); + var _segments = $("
", { "class": "enhanced-textarea-markup" }).appendTo(_markupMenu); + var _toolbar = $("
", { "class": "enhanced-textarea-markup-segment" }).appendTo(_segments); + var _menu = $("
", { "class": "enhanced-textarea-markup-segment" }).appendTo(_segments); + + if (opt.markup == "inline") + ar_AddInlineToolbar(ta.get(0), _toolbar.get(0), _menu.get(0)); + else + ar_AddToolbar(ta.get(0), _toolbar.get(0), _menu.get(0)); + + if (opt.dynamicResizeOption) { + var _dynResize = $("
", { "class": "enhanced-textarea-markup-segment" }).appendTo(_segments); + var _lblDynResize = $("