· 5 years ago · Mar 30, 2020, 11:20 AM
1/* jshint freeze:false, bitwise:false, unused:false */
2/* global Game, GPWindowMgr, ngettext, Timestamp, DebugTranslations, GameData */
3
4// TODO separate polyfills from common functions and native object extensions (should be undone if possible)
5
6if (!Date.now) { // Emulate Date.now() for IE8
7 Date.now = function() {
8 return new Date().valueOf();
9 };
10}
11
12if (!String.prototype.camelCase) {
13 String.prototype.camelCase = function() {
14 return this.replace(/(?:\b|_)(.)/g, function (x, chr) {
15 return chr.toUpperCase();
16 });
17 };
18}
19
20if (!String.prototype.snakeCase) {
21 String.prototype.snakeCase = function() {
22 return this.replace(/([A-Z])/g, function(capital) {
23 return '_' + capital.toLowerCase();
24 }).replace(/^_/, '');
25 };
26}
27
28//inheritance wrapper
29if (!Function.prototype.inherits) {
30 Function.prototype.inherits = function(F) {
31 this.prototype = new F();
32 this.prototype.constructor = this;
33 this.prototype.parent = F.prototype;
34 };
35}
36
37if (!Number.prototype.between) {
38 Number.prototype.between = function(a, b) {
39 return (a < b ? this >= a && this <= b : this >= b && this <= a);
40 };
41}
42
43if (!Array.prototype.searchFor) {
44 Array.prototype.searchFor = function(attr, value) {
45 return this.filter(function(el) {
46 return el[attr] == value; // jshint ignore:line
47 });
48 };
49}
50
51if (!String.prototype.truncate) {
52 String.prototype.truncate = function(limit) {
53 var len = this.length;
54
55 return len > limit ? this.substr(0, limit) + '...' : this;
56 };
57}
58
59if (!Array.prototype.remove) {
60 //Some array functions - By John Resig (MIT Licensed)
61 Array.prototype.remove = function(from, to) {
62 var rest = this.slice((to || from) + 1 || this.length);
63 this.length = from < 0 ? this.length + from : from;
64 return this.push.apply(this, rest);
65 };
66}
67
68if (!Array.prototype.max) {
69 Array.prototype.max = function(array){
70 return Math.max.apply(Math, array);
71 };
72}
73
74String.prototype.repeat = function (times) {
75 'use strict';
76
77 var out = '', i;
78
79 for (i = 0; i < times; i++) {
80 out += this;
81 }
82
83 return out;
84};
85
86/**
87 * us.min([1, 2]);
88 *
89 * @deprecated
90 */
91if (!Array.prototype.min) {
92 Array.prototype.min = function(array) {
93 return Math.min.apply(Math, array );
94 };
95}
96
97if (!Array.prototype.clone) {
98 Array.prototype.clone = function() {
99 return this.slice(0);
100 };
101}
102
103/**
104 * Strip HTML-Tags from a String
105 */
106if (!String.prototype.strip) {
107 String.prototype.strip = function () {
108 return this.replace(/<(.|\n)*?>/g,'');
109 };
110}
111
112/**
113 * Needed for IE10 because it doesn't support group function
114 */
115
116if (window.console && !window.console.group) {
117 window.console.group = function(str) {
118 return str;
119 };
120
121 window.console.groupEnd = function() {
122 return '';
123 };
124}
125
126/**
127 * Checks if a string is lte than another string. If both strings start with a number, those values are parsed and then compared.
128 *
129 * @param y {String}
130 */
131
132if (!String.prototype.isLTE) {
133 String.prototype.isLTE = function (y) {
134 //parse
135 var x = this,
136 x_m = x.match(/^\s+\d+|^\d+/),
137 y_m = y.match(/^\s+\d+|^\d+/),
138 x_i = x_m !== null ? parseInt(x_m.shift(),10) : NaN,
139 y_i = y_m !== null ? parseInt(y_m.shift(),10) : NaN;
140 //compare strings if one isNaN
141 if (isNaN(x_i) || isNaN(y_i)) {
142 x = x.toLowerCase();
143 y = y.toLowerCase();
144 } else {
145 x = x_i;
146 y = y_i;
147 }
148 //compare
149 return x <= y;
150 };
151}
152
153/**
154 * Returns the Date as a short string
155 */
156if (!Date.prototype.toShortString) {
157 Date.prototype.toShortString = function(){
158 var h, m, d, mn;
159 h = ( ( h = this.getUTCHours()) < 10 ? '0' + h : h );
160 m = ( ( m = this.getUTCMinutes()) < 10 ? '0' + m : m );
161 d = ( ( d = this.getUTCDate()) < 10 ? '0' + d : d );
162 mn = ( ( mn = this.getUTCMonth()+1) < 10 ? '0' + mn : mn );
163 return ( h + ':' + m + ' ' + d + '/' + mn + '/' + this.getUTCFullYear());
164 };
165}
166
167function utf8Encode(string) {
168 string = string.replace(/\r\n/g, '\n');
169 var utftext = '',
170 n;
171
172 for (n = 0; n < string.length; n++) {
173
174 var c = string.charCodeAt(n);
175
176 if (c < 128) {
177 utftext += String.fromCharCode(c);
178 }
179 else if((c > 127) && (c < 2048)) {
180 utftext += String.fromCharCode((c >> 6) | 192);
181 utftext += String.fromCharCode((c & 63) | 128);
182 }
183 else {
184 utftext += String.fromCharCode((c >> 12) | 224);
185 utftext += String.fromCharCode(((c >> 6) & 63) | 128);
186 utftext += String.fromCharCode((c & 63) | 128);
187 }
188
189 }
190
191 return utftext;
192}
193
194//private method for UTF-8 decoding
195function utf8Decode(utftext) {
196 var string = '';
197 var i = 0,
198 c, c3, c2;
199 c = c3 = c2 = 0;
200
201 while ( i < utftext.length ) {
202 c = utftext.charCodeAt(i);
203
204 if (c < 128) {
205 string += String.fromCharCode(c);
206 i++;
207 } else if ((c > 191) && (c < 224)) {
208 c2 = utftext.charCodeAt(i+1);
209 string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
210 i += 2;
211 } else {
212 c2 = utftext.charCodeAt(i+1);
213 c3 = utftext.charCodeAt(i+2);
214 string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
215 i += 3;
216 }
217 }
218
219 return string;
220}
221
222
223/**
224 * jQuery.blocker
225 *
226 * @param object options
227 * @return {self}
228 *
229 * minimum example:
230 * $.blocker({html: $('<div>This is an (visual) blocking note</div>')});
231 */
232jQuery.blocker = function(options){
233 var settings = jQuery.extend({
234 id: '', // rename to caching
235 caching: options.id,
236 html: 'f00',
237 width: 520,
238 height: 200,
239 gameloader: false,
240 bgClickable: true,
241 success: '',
242 cssClass : '',
243 onSuccess: function(){},
244 cancel: '',
245 onCancel: function(){},
246 callback: void(0)
247 }, options);
248
249 var block_wrapper = '<div class="gpwindow_frame ' + settings.cssClass + '" style="position: absolute; top: ' + (($(window).height() - settings.height) / 2) + 'px;' +
250 'left: ' + (($(window).width() - settings.width) / 2) + 'px; width: ' + settings.width + 'px; height: ' + settings.height + 'px; z-index: 5000; display: block;">' +
251 '<div class="gpwindow_left"></div><div class="gpwindow_right"></div><div class="gpwindow_bottom"><div class="gpwindow_left corner"></div><div class="gpwindow_right corner">'+
252 '</div></div><div class="gpwindow_top"><div class="gpwindow_left corner"></div><div class="gpwindow_right corner"></div></div>' +
253 '<div class="gpwindow_content"></div>';
254 var frame = $(block_wrapper);
255
256 var elm = {
257 box: frame,
258 bg: $('<div id="blockbox_bg"></div>'),
259 body: $('body')
260 };
261
262 elm.content = elm.box.find('div.gpwindow_content');
263
264 // show
265 this.blocker.block = function(){
266 //save location ... otherwise use document body
267 var tmp = settings.html.parent();
268 elm.original_parent = tmp.length ? tmp : $('body');
269
270 elm.html = settings.html.detach();
271 elm.content.append(elm.html.show());
272
273 elm.box.appendTo(elm.body).show();
274 elm.bg.appendTo(elm.body).show();
275 };
276
277 this.blocker.handleEvents = function(){
278 // set handlers
279 if(settings.bgClickable){
280 elm.bg.bind('click.block', function(){
281 jQuery.blocker.unblock();
282 });
283 }
284 // bind user defined functions to elements
285 $(settings.success).unbind('click').click(function(){
286 settings.onSuccess();
287 jQuery.blocker.unblock();
288 });
289 $(settings.cancel).unbind('click').click(function(){
290 settings.onCancel();
291 jQuery.blocker.unblock();
292 });
293 };
294
295 this.blocker.unblock = function(){
296 elm.box.hide().detach();
297 elm.bg.hide().detach();
298 if(settings.gameloader){
299 elm.html.remove();
300 } else {
301 elm.html.appendTo(elm.original_parent).hide();
302 }
303 if(settings.callback && typeof settings.callback === 'function'){
304 settings.callback();
305 }
306 };
307
308 this.blocker.block();
309 this.blocker.handleEvents();
310
311 return this.blocker;
312};
313
314/**
315 * @param controller String
316 * @param parameters Object
317 */
318function buildLink(controller, parameters) {
319 var params = [],
320 key;
321 for(key in parameters) {
322 params.push(key + '=' + escape(parameters[key]));
323 }
324 // always append the CSRF token
325 params.push('h=' + encodeURIComponent(Game.csrfToken));
326
327 return '/game/' + controller + '?' + params.join('&');
328}
329/**
330 * Generates a link from the given parameters.
331 *
332 * @param controller String
333 * @param action String
334 * @param parameters Object
335 * @return String
336 */
337function url(controller, action, parameters) {
338 if (controller && controller.substr(0, 1) === '/') {
339 return controller;
340 }
341 controller = controller || Game.controller;
342 parameters = parameters || {};
343
344 if (typeof action !== 'undefined' && action !== '') {
345 parameters.action = action;
346 }
347 parameters.town_id = parameters.town_id || Game.townId;
348
349 return buildLink(controller, parameters);
350}
351
352/**
353 * Transforms seconds to hours:minutes:seconds
354 * @param {number} duration in seconds
355 * @param {boolean} only_non_zero - if true, omits any leading 0s
356 * @return {String}
357 */
358function day_hr_min_sec(duration, only_non_zero, options) {
359 var result, days, hours, minutes, seconds, hours_str, minutes_str, seconds_str,
360 lang = {
361 days : __('day|d'),
362 hours : __('hour|h'),
363 minutes : __('minute|m'),
364 seconds : __('second|s')
365 };
366
367 function getSecondsHtml() {
368 return with_seconds ? (seconds_str + '<span>' + lang.seconds+'</span>') : '';
369 }
370
371 options = options || {};
372 var with_seconds = typeof options.with_seconds === "undefined" ? true : options.with_seconds;
373
374 if (duration === 0) {
375 return _('Now');
376 }
377
378 days = parseInt(duration / 86400, 10);
379 hours = parseInt((duration / 3600) % 24, 10);
380 minutes = parseInt((duration / 60) % 60, 10);
381 seconds = parseInt(duration % 60, 10);
382
383 //Days won't be displayed when == 0
384 result = days > 0 ? days + '<span>' + lang.days + '</span> ' : '';
385
386 //Add 0 at the beginning if number is smaller than 10
387 hours_str = (hours < 10) ? '0' + hours : hours;
388 minutes_str = (minutes < 10) ? '0' + minutes : minutes;
389 seconds_str = (seconds < 10) ? '0' + seconds : seconds;
390
391 if (only_non_zero) {
392 result += (hours > 0 || days > 0) ? (hours_str + '<span>' + lang.hours + '</span> ') : '';
393 result += (minutes > 0 || hours > 0 || days > 0) ? (minutes_str + '<span>' + lang.minutes + '</span> ') : '';
394 result += getSecondsHtml();
395 } else {
396 result += hours_str + '<span>' + lang.hours + '</span> ' +
397 minutes_str + '<span>' + lang.minutes + '</span> ' +
398 getSecondsHtml();
399 }
400
401 return result;
402}
403
404/**
405 * Transforms seconds to x hours (y minutes) (z seconds)
406 * primary used for inline texts e.g. in power effects
407 *
408 * @param {integer} duration
409 * @see TimeFormat.php formatTime()
410 * @return {string}
411 */
412function hours_minutes_seconds(duration) {
413 var weeks, days, hours, minutes, seconds,
414 str = [];
415
416 weeks = Math.floor(duration / 604800);
417 days = Math.floor((duration / 86400) % 7);
418 hours = Math.floor((duration / 3600) % 24);
419 minutes = Math.floor((duration / 60) % 60);
420 seconds = Math.floor(duration % 60);
421
422 if (weeks > 0) {
423 str.push(weeks + ' ' + ngettext('week', 'weeks', weeks));
424 }
425 if (days > 0) {
426 str.push(days + ' ' + ngettext('day', 'days', days));
427 }
428 if (hours > 0) {
429 str.push(hours + ' ' + ngettext('hour', 'hours', hours));
430 }
431 if (minutes > 0) {
432 str.push(minutes + ' ' + ngettext('minute', 'minutes', minutes));
433 }
434 if (seconds > 0) {
435 str.push(seconds + ' ' + ngettext('second', 'seconds', seconds));
436 }
437
438 return str.join(' ');
439}
440
441/**
442 * Transforms date object to readable format
443 *
444 * @param object date
445 * @param boolean date is utc
446 * @param boolean ext_date - flag if a date with day and month should be returned
447 * @param boolean with_seconds - flag if a date with seconds should be returned
448 * @return String
449 */
450function readableDate(date, utc, ext_date, with_seconds) {
451 return readableUnixTimestamp(date.getTime(), (utc === true ? 'no_offset' : 'player_timezone'), {extended_date: ext_date, with_seconds: with_seconds});
452}
453
454/**
455 * Transforms a unix timestamp to readable format
456 * This readable format could depend on a timezone:
457 * - player_timezone := player timezone from his settings (default)
458 * - lc_timezone := servers lc_timezone
459 * - no_offset := no timezone offset
460 *
461 * The options object can hold of those two boolean flags
462 * - extended_date := flag if a date with day and month should be returned
463 * - with_seconds := flag if a date with seconds should be returned
464 *
465 * @param {integer} timestamp
466 * @param {string} timezone_type
467 * @param {object} options
468 * @return String
469 */
470function readableUnixTimestamp(timestamp, timezone_type, options) {
471 options = options === undefined ? {} : options;
472
473 var with_seconds = options.with_seconds === undefined ? true : options.with_seconds;
474 var extended_date = options.extended_date === undefined ? false : options.extended_date;
475
476 var ts = (timezone_type === 'no_offset' ? timestamp : Timestamp.shiftUnixTimestampByTimezoneOffset(timestamp, timezone_type));
477
478 var date = new Date(ts * 1E3);
479 var hours, minutes, seconds, days, months;
480 var result;
481
482 hours = date.getUTCHours();
483 minutes = date.getUTCMinutes();
484 seconds = date.getUTCSeconds();
485
486 if (minutes < 10) {
487 minutes = '0' + minutes;
488 }
489
490 if (seconds < 10) {
491 seconds = '0' + seconds;
492 }
493
494 if (extended_date) {
495 days = date.getUTCDate();
496 months = date.getUTCMonth() + 1;
497
498 if (days < 10) {
499 days = '0' + days;
500 }
501
502 if (months < 10) {
503 months = '0' + months;
504 }
505
506 result = days + '.' + months + '.|' + hours + ':' + minutes + (with_seconds ? (':' + seconds) : '');
507 } else {
508 result = hours + ':' + minutes + (with_seconds ? (':' + seconds) : '');
509 }
510
511 return result;
512}
513/**
514 * Returns a ratio string from a given float.
515 * One side of the ratio is 1.
516 * The other side is always >=1.
517 * e.g.
518 * 1.2 -> '1.2':1
519 * 0.8 -> '1:1.2'
520 *
521 * @param {number} fraction
522 * @return {string}
523 */
524function readableRatio(fraction) {
525 var bigger_part = Math.round( (fraction >= 1 ? fraction : (1/fraction)) * 100 ) / 100;
526 if (fraction < 1) {
527 return '1:' + bigger_part;
528 } else {
529 return bigger_part + ':1';
530 }
531}
532
533// Translation tool, return s
534// Strings wrapped with this will get translated
535function _(s) {
536 if (DebugTranslations.isEnabled()) {
537 return DebugTranslations.markString(s);
538 }
539
540 return s;
541}
542
543// Translation tool, return s
544// Strings wrapped with this will NOT get translated
545function _literal(s) {
546 return s;
547}
548
549// context aware gettext alias - only used for dev
550// and fallback in case of missing translation
551function __(s) {
552 if (DebugTranslations.isEnabled()) {
553 return DebugTranslations.markString(s.substr(s.indexOf('|')+1));
554 }
555
556 return s.substr(s.indexOf('|')+1);
557}
558
559function s(text) {
560 var i;
561 if(!text){
562 return '';
563 }
564 for(i = 1; i<arguments.length; i++) {
565 text = text.split('%'+i).join(arguments[i]);
566 }
567
568 return text;
569}
570
571/**
572 * Debugging function.
573 *
574 * @param whatever Object
575 */
576function debug(whatever){
577 var i, s;
578 if(!Game.dev) {
579 return;
580 }
581 try {
582 if(arguments.length > 1){
583 console.group();
584 for(i = 0; i < arguments.length; i++){
585 console.debug(arguments[i]);
586 }
587 console.groupEnd();
588 } else {
589 console.log(whatever);
590 }
591 } catch(x){
592 try {
593 opera.postError(whatever);
594 } catch(y){
595 if ('object' === typeof(whatever)) {
596 s = '';
597 for (var i in whatever){
598 s += i + ': ' + whatever[i] + '\n';
599 }
600 alert(s);
601 } else {
602 alert(whatever);
603 }
604 }
605 }
606}
607
608jQuery.extend(jQuery.easing,{
609 // t: current time, b: beginning value, c: change in value, d: duration
610 bounce: function (x, t, b, c, d) {
611 if ((t/=d) < (1/2.75)) {
612 return c*(7.5625*t*t) + b;
613 } else if (t < (2/2.75)) {
614 return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
615 } else if (t < (2.5/2.75)) {
616 return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
617 } else {
618 return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
619 }
620 }
621});
622
623/**
624 * @deprecated, use .bind()/.click()/.whatever() instead.
625 */
626function w(foo){
627 var elm = foo || this;
628 if (elm.wnd) {
629 return elm.wnd;
630 }
631
632 while (elm.parentNode &&
633 // if element has a tagName different from 'DIV', we skip it. This is mainly a workaround
634 // for <form>-elements, which cause serious problems when used with this function, e. g.:
635 // an error is thrown with something like: form#[object HTMLInputElement]
636 ((elm = elm.parentNode).tagName !== 'DIV' ? elm = elm.parentNode : elm).id.indexOf('gpwnd') === -1){
637 null;
638 }
639
640 var id = (~~elm.id.match(/\d+/));
641 elm.wnd = GPWindowMgr.GetByID(id);
642 return elm.wnd;
643}
644
645function attr(attribs){
646 var res = '',
647 att;
648
649 for (att in attribs) {
650 if (attribs[att]) {
651 res = res + ' ' + att + '="' + attribs[att] + '"';
652 }
653 }
654 return res;
655}
656
657/**
658 * function to create a button as string or jquery object
659 *
660 * @param string text
661 * @param object html_options
662 * @param boolean as_string (optional: default false)
663 * @return object either string or jquery object
664 */
665function button(text, html_options, as_string, wnd) {
666 var btn,
667 button_text,
668 callback,
669 css_class = 'button';
670
671 if (html_options.hasOwnProperty('class')) {
672 css_class += ' ' + html_options['class'];
673 }
674
675 if (!html_options.href) {
676 html_options.href = '#';
677 }
678 if ((callback = html_options.onclick) && typeof callback === 'function') {
679 // if we bind a function, we cannot return a string
680 as_string = false;
681 delete html_options.onclick;
682 }
683
684 html_options = attr(html_options);
685
686 button_text = '<a class="' + css_class + '" ' + html_options + '>'+
687 '<span class="left"><span class="right">'+
688 '<span class="middle">' + text + '</span>'+
689 '</span></span>'+
690 '<span style="clear:both;"></span>' +
691 '</a>';
692
693 if (!as_string) {
694 btn = $(button_text);
695 }
696 // if callback insted of onclick:
697 if (callback && !as_string) {
698 btn.click(function() {
699 callback.apply(wnd);
700 });
701 }
702
703 return as_string ? button_text : btn;
704}
705
706
707/**
708 * checks if argument is a number
709 *
710 * @param object val
711 * @return boolean
712 */
713function isNumber(val){
714 return typeof val === 'number' && isFinite(val);
715}
716
717/**
718 * checks if argument is an array
719 *
720 * @param {object} o
721 * @return boolean
722 */
723function is_array(o){
724 return typeof o === 'object' && (o instanceof Array);
725}
726
727/**
728 * adds slashes to string
729 *
730 * @param str string
731 * @return string
732 */
733function addslashes(str) {
734 var result = str || '';
735 result = result.replace(/\\/g,'\\\\');
736 result = result.replace(/\'/g,'\\\'');
737 result = result.replace(/\"/g,'\\"');
738 result = result.replace(/\0/g,'\\0');
739 return result;
740}
741
742/**
743 * get human readable time and date format: hours:minutes:seconds day/month/year
744 */
745function getHumanReadableTimeDate(time) {
746 // http://www.hunlock.com/blogs/Javascript_Dates-The_Complete_Reference
747 var hours = time.getUTCHours(),
748 minutes = time.getUTCMinutes(),
749 seconds = time.getUTCSeconds(),
750 day = time.getUTCDate(),
751 month = time.getUTCMonth() + 1,
752 year = time.getUTCFullYear();
753
754 if (hours < 10) {
755 hours = '0' + hours;
756 }
757 if (minutes < 10) {
758 minutes = '0' + minutes;
759 }
760 if (seconds < 10) {
761 seconds = '0' + seconds;
762 }
763 if (day < 10) {
764 day = '0' + day;
765 }
766 if (month < 10) {
767 month = '0' + month;
768 }
769 return hours + ':' + minutes + ':' + seconds + ' ' + day + '/' + month + '/' + year;
770}
771
772/**
773 * get human readable date format: day/month/year
774 */
775function getHumanReadableDate(timestamp) {
776 var day = timestamp.getUTCDate(),
777 month = timestamp.getUTCMonth() + 1,
778 year = timestamp.getUTCFullYear();
779
780 if (day < 10) {
781 day = '0' + day;
782 }
783 if (month < 10) {
784 month = '0' + month;
785 }
786 return day + '/' + month + '/' + year;
787}
788
789
790/**
791 * creates a date object by the date arguments supplied. A concrete timezone
792 * for the date supplied can be specified
793 *
794 * @param {integer} year
795 * @param {integer} month
796 * @param {integer} day
797 * @param {integer} hours
798 * @param {integer} minutes
799 * @param {integer} seconds
800 * @param {integer} timezone_offset (in seconds) GMT +02 = 7200
801 *
802 * @return object date
803 */
804function newDateByTimezone(year, month, day, hours, minutes, seconds, timezone_offset){
805 var date = new Date();
806 date.setUTCFullYear(year, month, day);
807 date.setUTCHours(hours, minutes, seconds - timezone_offset);
808
809 return date;
810}
811
812
813/**
814 * Only used in wndhandler_attack and building_place
815 * TODO: get rid of this method in common.js
816 * @param units Array
817 * @param data Object unit data for current town
818 * @param pop jQuery element for display of selected population
819 * @param cap jQuery element for display of total capacity
820 * @param progbar jQuery progressbar element
821 */
822function recalcCapacity(units, researches, pop, cap, progbar, slow_boats_needed, fast_boats_needed) {
823 researches = researches || 0;
824
825 var q, progress,
826 total_capacity = 0,
827 total_population = 0,
828 // TODO!
829 pwidth = 460,
830 berth = researches.berth || 0,
831 gdunits = GameData.units,
832 big_transporter_cap = gdunits.big_transporter.capacity,
833 small_transporter_cap = gdunits.small_transporter.capacity;
834
835 units.each(function() {
836 var i = this.name,
837 count = ~~this.value,
838 cap;
839
840 // don't do anything if 0 or a flying unit
841 if (count && !gdunits[i].flying) {
842 if (gdunits[i].is_naval) {
843 // add research-modifiers if they exist
844 total_capacity += ((cap = gdunits[i].capacity) + (cap ? researches.berth || 0 : 0)) * count;
845 } else {
846 total_population += gdunits[i].population * count;
847 }
848 }
849 });
850
851 q = total_population / total_capacity;
852 progress = ( q >= 1 || !total_capacity) ? 0 : (1 - q) * -pwidth;
853
854 pop.text(total_population);
855 cap.text(total_capacity);
856 progbar.stop(true,true).animate({backgroundPosition: progress + 'px'});
857
858 if (slow_boats_needed) {
859 slow_boats_needed.text(Math.ceil(total_population / (big_transporter_cap + berth)));
860 }
861
862 if (fast_boats_needed) {
863 fast_boats_needed.text(Math.ceil(total_population / (small_transporter_cap + berth)));
864 }
865
866 return {
867 'capacity': total_capacity,
868 'population': total_population
869 };
870}
871
872function isSomeThing(checks, anyOrAll) {
873 var isSomeThing, uagent;
874
875 return function() {
876 var check_idx, checks_length = checks.length, check;
877
878 if (typeof isSomeThing === 'boolean') {
879 return isSomeThing;
880 }
881 else {
882 uagent = navigator.userAgent.toLowerCase();
883 if (anyOrAll === 'any') {
884 for (check_idx = 0; check_idx < checks_length; ++check_idx) {
885 check = checks[check_idx];
886 isSomeThing = isSomeThing || uagent.indexOf(check) > -1;
887 }
888 } else {
889 isSomeThing = true;
890 for (check_idx = 0; check_idx < checks_length; ++check_idx) {
891 check = checks[check_idx];
892 isSomeThing = isSomeThing && uagent.indexOf(check) > -1;
893 }
894 }
895
896 return isSomeThing;
897 }
898 };
899}
900
901var isiOs = isSomeThing(['iphone', 'ipad', 'ipod'], 'any');
902var isIeTouch = isSomeThing(['touch', 'trident', 'tablet'], 'all');
903var isMsApp = isSomeThing(['trident', 'msapphost'], 'all');
904
905function isSmallScreen() {
906
907 if (isSmallScreen.result) {
908 return isSmallScreen.result;
909 }
910
911 if ($(window).height() <= 800) {
912 isSmallScreen.result = true;
913 } else {
914 isSmallScreen.result = false;
915 }
916
917 return isSmallScreen.result;
918}
919
920/**
921 * Generated a DWORD (32-bit-integer) from 2 WORDS (16 bit ints)
922 *
923 * @param {Number} lo Lower Half WORD
924 * @param {Number} hi Higher Half WORD
925 * @return {Number} generated DWORD
926 */
927function makeDWord(lo, hi) {
928 return (lo & 0xffff ) | ((hi & 0xffff) << 16);
929}
930
931var Sort = {
932 sort_by: null,
933
934 /**
935 * Setter method for the values by which the list should be sorted
936 *
937 * @param {String} string classname of any sortable value
938 */
939 sortBy: function (string) {
940 this.sort_by = string;
941 },
942 /**
943 * checks if list is already sorted by a value
944 */
945 sortedBy: function(string) {
946 return this.sort_by === string;
947 },
948
949 /**
950 * Sorts an array via quicksort. sortBy() must be used first.
951 *
952 * @param {Array} array The array which has to be sorted
953 */
954 qsort: function(array) {
955 var greater = [], less = [];
956 if (!array || array.length <= 1) {
957 return array;
958 }
959 else {
960 var index = Math.floor(Math.random() * (array.length - 1));
961 var pivot = array[index], i;
962 array.splice(index,1);
963 for (i = 0; i < array.length; i++) {
964 var obj = array[i];
965 var x = $(obj).find('span.sortable.' + this.sort_by).text();
966 var y = $(pivot).find('span.sortable.' + this.sort_by).text();
967
968 if (x.isLTE(y)) {
969 less.push(obj);
970 }
971 else {
972 greater.push(obj);
973 }
974 }
975 return (this.qsort(less).concat(pivot)).concat(this.qsort(greater));
976 }
977 }
978};
979
980/**
981 * change digit to roman numerals (up to 4000)
982 */
983function romanNumerals(val) {
984 var values = [1000, 900, 500, 400, 100,90, 50, 40, 10, 9, 5, 4, 1],
985 numerals = ['min', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'],
986 vl = values.length,
987 i = 0,
988 rest = val,
989 return_val = '';
990
991 for (; i < vl; ++i) {
992 while (rest > values[i]) {
993 rest -= values[i];
994 return_val += numerals[i];
995 }
996 }
997
998 return return_val;
999}
1000
1001if (!window.getComputedStyle) {
1002 window.getIEComputedStyle = function(el, pseudo) {
1003 this.el = el;
1004 this.getPropertyValue = function(prop) {
1005 var re = /(\-([a-z]){1})/g;
1006 if (prop === 'float') {
1007 prop = 'styleFloat';
1008 }
1009 if (re.test(prop)) {
1010 prop = prop.replace(re, function () {
1011 return arguments[2].toUpperCase();
1012 });
1013 }
1014 return el.currentStyle[prop] ? el.currentStyle[prop] : null;
1015 };
1016 this.cssText = function() {
1017 if(!el) {
1018 return false;
1019 }
1020 var elStyle = el.currentStyle,
1021 cssText = '';
1022 for (var k in elStyle) { cssText += k + ':' + elStyle[k] + ';';}
1023 return cssText.replace(/([A-Z])/g, function($1){return '-'+$1.toLowerCase();});
1024 }();
1025
1026 return this;
1027 };
1028}
1029
1030function difference(template, override) {
1031 var ret = {};
1032 for (var name in template) {
1033 if (template.hasOwnProperty(name) && name in override) {
1034 if (us.isObject(override[name]) && !us.isArray(override[name])) {
1035 var diff = difference(template[name], override[name]);
1036 if (!us.isEmpty(diff)) {
1037 ret[name] = diff;
1038 }
1039 } else if (!us.isEqual(template[name], override[name])) {
1040 ret[name] = override[name];
1041 }
1042 }
1043 }
1044 return ret;
1045}
1046
1047/**
1048 * Shortens a string exceeding the total_max_length with an ellipsis (3 dots)
1049 * total_max_length includes the length of the ellipsis!
1050 *
1051 * @param {String} string_to_shorten
1052 * @param {number} total_max_length
1053 */
1054function ellipsis(string_to_shorten, total_max_length) {
1055 if (string_to_shorten.length <= total_max_length) {
1056 return string_to_shorten;
1057 }
1058
1059 return string_to_shorten.slice(0, total_max_length - 1) + '\u2026';
1060}
1061/**
1062 * Wrapper for the native number.toLocaleString() in order to make sure the game locale is used.
1063 * @param number
1064 * @return {string|*}
1065 */
1066function numberToLocaleString(number) {
1067 var toLocaleStringSupportsLocales = function() {
1068 var test_number = 0;
1069 try {
1070 test_number.toLocaleString('i');
1071 } catch (e) {
1072 return e.name === 'RangeError';
1073 }
1074 return false;
1075 },
1076 locale = Game.locale_lang.replace('_','-');
1077
1078 return toLocaleStringSupportsLocales() ?
1079 number.toLocaleString(locale) :
1080 number;
1081}
1082
1083(function($) {
1084 'use strict';
1085
1086 /**
1087 * When element is hidden with display:none; we can not get its proper size
1088 * in this case, we have to show him for a while somewhere out of the viewport,
1089 * take the size, and restore values
1090 */
1091 $.fn.hiddenOuterWidth = function(include_margin) {
1092 include_margin = include_margin ? true : false;
1093
1094 var $el = $(this),
1095 old_styles = {
1096 left : $el.css('left'),
1097 display : $el.css('display'),
1098 position : $el.css('position'),
1099 visibility : $el.css('visibility'),
1100 width : $el.css('width')
1101 }, outer_width;
1102
1103 $el.css({left : 300, display : 'block', position : 'absolute', visibility: 'visible'});
1104
1105 outer_width = $el.outerWidth(include_margin);
1106
1107 $el.css(old_styles);
1108
1109 return outer_width;
1110 };
1111
1112 /**
1113 *
1114 */
1115 $.fn.setOffsetWidth = function(value) {
1116 var $el = $(this),
1117 margins_width = $el.outerWidth(true) - $el.outerWidth();
1118
1119 $el.width(value - margins_width);
1120
1121 return this;
1122 };
1123}(jQuery));