--- /dev/null
+Ext.ns('Proxmox');
+Ext.ns('Proxmox.Setup');
+
+// TODO: implement gettext
+function gettext(buf) { return buf; }
+
+
+if (!Ext.isDefined(Proxmox.Setup.auth_cookie)) {
+ trow "Proxmox library not initialize";
+}
+
+// avoid errors related to Accessible Rich Internet Applications
+// (access for people with disabilities)
+// TODO reenable after all components are upgraded
+Ext.enableAria = false;
+Ext.enableAriaButtons = false;
+Ext.enableAriaPanels = false;
+
+// avoid errors when running without development tools
+if (!Ext.isDefined(Ext.global.console)) {
+ var console = {
+ dir: function() {},
+ log: function() {}
+ };
+}
+
+Ext.Ajax.defaultHeaders = {
+ 'Accept': 'application/json'
+};
+
+Ext.Ajax.on('beforerequest', function(conn, options) {
+ if (Proxmox.CSRFPreventionToken) {
+ if (!options.headers) {
+ options.headers = {};
+ }
+ options.headers.CSRFPreventionToken = Proxmox.CSRFPreventionToken;
+ }
+});
+
+Ext.define('Proxmox.Utils', { utilities: {
+
+ // this singleton contains miscellaneous utilities
+
+ authOK: function() {
+ return (Proxmox.UserName !== '') && Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name);
+ },
+
+ authClear: function() {
+ Ext.util.Cookies.clear(Proxmox.Setup.auth_cookie_name);
+ },
+
+ // comp.setLoading() is buggy in ExtJS 4.0.7, so we
+ // use el.mask() instead
+ setErrorMask: function(comp, msg) {
+ var el = comp.el;
+ if (!el) {
+ return;
+ }
+ if (!msg) {
+ el.unmask();
+ } else {
+ if (msg === true) {
+ el.mask(gettext("Loading..."));
+ } else {
+ el.mask(msg);
+ }
+ }
+ },
+
+ monStoreErrors: function(me, store) {
+ me.mon(store, 'beforeload', function(s, operation, eOpts) {
+ if (!me.loadCount) {
+ me.loadCount = 0; // make sure it is numeric
+ Proxmox.Utils.setErrorMask(me, true);
+ }
+ });
+
+ // only works with 'proxmox' proxy
+ me.mon(store.proxy, 'afterload', function(proxy, request, success) {
+ me.loadCount++;
+
+ if (success) {
+ Proxmox.Utils.setErrorMask(me, false);
+ return;
+ }
+
+ var msg;
+ /*jslint nomen: true */
+ var operation = request._operation;
+ var error = operation.getError();
+ if (error.statusText) {
+ msg = error.statusText + ' (' + error.status + ')';
+ } else {
+ msg = gettext('Connection error');
+ }
+ Proxmox.Utils.setErrorMask(me, msg);
+ });
+ },
+
+ extractRequestError: function(result, verbose) {
+ var msg = gettext('Successful');
+
+ if (!result.success) {
+ msg = gettext("Unknown error");
+ if (result.message) {
+ msg = result.message;
+ if (result.status) {
+ msg += ' (' + result.status + ')';
+ }
+ }
+ if (verbose && Ext.isObject(result.errors)) {
+ msg += "<br>";
+ Ext.Object.each(result.errors, function(prop, desc) {
+ msg += "<br><b>" + Ext.htmlEncode(prop) + "</b>: " +
+ Ext.htmlEncode(desc);
+ });
+ }
+ }
+
+ return msg;
+ },
+
+ // Ext.Ajax.request
+ API2Request: function(reqOpts) {
+
+ var newopts = Ext.apply({
+ waitMsg: gettext('Please wait...')
+ }, reqOpts);
+
+ if (!newopts.url.match(/^\/api2/)) {
+ newopts.url = '/api2/extjs' + newopts.url;
+ }
+ delete newopts.callback;
+
+ var createWrapper = function(successFn, callbackFn, failureFn) {
+ Ext.apply(newopts, {
+ success: function(response, options) {
+ if (options.waitMsgTarget) {
+ options.waitMsgTarget.setLoading(false);
+ }
+ var result = Ext.decode(response.responseText);
+ response.result = result;
+ if (!result.success) {
+ response.htmlStatus = Proxmox.Utils.extractRequestError(result, true);
+ Ext.callback(callbackFn, options.scope, [options, false, response]);
+ Ext.callback(failureFn, options.scope, [response, options]);
+ return;
+ }
+ Ext.callback(callbackFn, options.scope, [options, true, response]);
+ Ext.callback(successFn, options.scope, [response, options]);
+ },
+ failure: function(response, options) {
+ if (options.waitMsgTarget) {
+ options.waitMsgTarget.setLoading(false);
+ }
+ response.result = {};
+ try {
+ response.result = Ext.decode(response.responseText);
+ } catch(e) {}
+ var msg = gettext('Connection error') + ' - server offline?';
+ if (response.aborted) {
+ msg = gettext('Connection error') + ' - aborted.';
+ } else if (response.timedout) {
+ msg = gettext('Connection error') + ' - Timeout.';
+ } else if (response.status && response.statusText) {
+ msg = gettext('Connection error') + ' ' + response.status + ': ' + response.statusText;
+ }
+ response.htmlStatus = msg;
+ Ext.callback(callbackFn, options.scope, [options, false, response]);
+ Ext.callback(failureFn, options.scope, [response, options]);
+ }
+ });
+ };
+
+ createWrapper(reqOpts.success, reqOpts.callback, reqOpts.failure);
+
+ var target = newopts.waitMsgTarget;
+ if (target) {
+ // Note: ExtJS bug - this does not work when component is not rendered
+ target.setLoading(newopts.waitMsg);
+ }
+ Ext.Ajax.request(newopts);
+ },
+
+ },
+
+ singleton: true,
+ constructor: function() {
+ var me = this;
+ Ext.apply(me, me.utilities);
+
+ var IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])";
+ var IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")";
+ var IPV6_H16 = "(?:[0-9a-fA-F]{1,4})";
+ var IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")";
+
+
+ me.IP4_match = new RegExp("^(?:" + IPV4_REGEXP + ")$");
+ me.IP4_cidr_match = new RegExp("^(?:" + IPV4_REGEXP + ")\/([0-9]{1,2})$");
+
+ var IPV6_REGEXP = "(?:" +
+ "(?:(?:" + "(?:" + IPV6_H16 + ":){6})" + IPV6_LS32 + ")|" +
+ "(?:(?:" + "::" + "(?:" + IPV6_H16 + ":){5})" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){4})" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,1}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){3})" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,2}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){2})" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,3}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){1})" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,4}" + IPV6_H16 + ")?::" + ")" + IPV6_LS32 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,5}" + IPV6_H16 + ")?::" + ")" + IPV6_H16 + ")|" +
+ "(?:(?:(?:(?:" + IPV6_H16 + ":){0,7}" + IPV6_H16 + ")?::" + ")" + ")" +
+ ")";
+
+ me.IP6_match = new RegExp("^(?:" + IPV6_REGEXP + ")$");
+ me.IP6_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + ")\/([0-9]{1,3})$");
+ me.IP6_bracket_match = new RegExp("^\\[(" + IPV6_REGEXP + ")\\]");
+
+ me.IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$");
+
+ var DnsName_REGEXP = "(?:(([a-zA-Z0-9]([a-zA-Z0-9\\-]*[a-zA-Z0-9])?)\\.)*([A-Za-z0-9]([A-Za-z0-9\\-]*[A-Za-z0-9])?))";
+ me.DnsName_match = new RegExp("^" + DnsName_REGEXP + "$");
+
+ me.HostPort_match = new RegExp("^(" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")(:\\d+)?$");
+ me.HostPortBrackets_match = new RegExp("^\\[(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")\\](:\\d+)?$");
+ me.IP6_dotnotation_match = new RegExp("^" + IPV6_REGEXP + "(\\.\\d+)?$");
+ }
+});
--- /dev/null
+/*
+ * The DiffStore is a in-memory store acting as proxy between a real store
+ * instance and a component.
+ * Its purpose is to redisplay the component *only* if the data has been changed
+ * inside the real store, to avoid the annoying visual flickering of using
+ * the real store directly.
+ *
+ * Implementation:
+ * The DiffStore monitors via mon() the 'load' events sent by the real store.
+ * On each 'load' event, the DiffStore compares its own content with the target
+ * store (call to cond_add_item()) and then fires a 'refresh' event.
+ * The 'refresh' event will automatically trigger a view refresh on the component
+ * who binds to this store.
+ */
+
+/* Config properties:
+ * rstore: the realstore which will autorefresh its content from the API
+ * Only works if rstore has a model and use 'idProperty'
+ * sortAfterUpdate: sort the diffstore before rendering the view
+ */
+Ext.define('Proxmox.data.DiffStore', {
+ extend: 'Ext.data.Store',
+ alias: 'store.diff',
+
+ sortAfterUpdate: false,
+
+ constructor: function(config) {
+ var me = this;
+
+ config = config || {};
+
+ if (!config.rstore) {
+ throw "no rstore specified";
+ }
+
+ if (!config.rstore.model) {
+ throw "no rstore model specified";
+ }
+
+ var rstore = config.rstore;
+
+ Ext.apply(config, {
+ model: rstore.model,
+ proxy: { type: 'memory' }
+ });
+
+ me.callParent([config]);
+
+ var first_load = true;
+
+ var cond_add_item = function(data, id) {
+ var olditem = me.getById(id);
+ if (olditem) {
+ olditem.beginEdit();
+ Ext.Array.each(me.model.prototype.fields, function(field) {
+ if (olditem.data[field.name] !== data[field.name]) {
+ olditem.set(field.name, data[field.name]);
+ }
+ });
+ olditem.endEdit(true);
+ olditem.commit();
+ } else {
+ var newrec = Ext.create(me.model, data);
+ var pos = (me.appendAtStart && !first_load) ? 0 : me.data.length;
+ me.insert(pos, newrec);
+ }
+ };
+
+ var loadFn = function(s, records, success) {
+
+ if (!success) {
+ return;
+ }
+
+ me.suspendEvents();
+
+ // getSource returns null if data is not filtered
+ // if it is filtered it returns all records
+ var allItems = me.getData().getSource() || me.getData();
+
+ // remove vanished items
+ allItems.each(function(olditem) {
+ var item = rstore.getById(olditem.getId());
+ if (!item) {
+ me.remove(olditem);
+ }
+ });
+
+ rstore.each(function(item) {
+ cond_add_item(item.data, item.getId());
+ });
+
+ me.filter();
+
+ if (me.sortAfterUpdate) {
+ me.sort();
+ }
+
+ first_load = false;
+
+ me.resumeEvents();
+ me.fireEvent('refresh', me);
+ me.fireEvent('datachanged', me);
+ };
+
+ if (rstore.isLoaded()) {
+ // if store is already loaded,
+ // insert items instantly
+ loadFn(rstore, [], true);
+ }
+
+ me.mon(rstore, 'load', loadFn);
+ }
+});
--- /dev/null
+/* This store encapsulates data items which are organized as an Array of key-values Objects
+ * ie data[0] contains something like {key: "keyboard", value: "da"}
+*
+* Designed to work with the KeyValue model and the JsonObject data reader
+*/
+Ext.define('Proxmox.data.ObjectStore', {
+ extend: 'Proxmox.data.UpdateStore',
+
+ constructor: function(config) {
+ var me = this;
+
+ config = config || {};
+
+ if (!config.storeid) {
+ config.storeid = 'proxmox-store-' + (++Ext.idSeed);
+ }
+
+ Ext.applyIf(config, {
+ model: 'KeyValue',
+ proxy: {
+ type: 'proxmox',
+ url: config.url,
+ extraParams: config.extraParams,
+ reader: {
+ type: 'jsonobject',
+ rows: config.rows,
+ readArray: config.readArray,
+ rootProperty: config.root || 'data'
+ }
+ }
+ });
+
+ me.callParent([config]);
+ }
+});
--- /dev/null
+Ext.define('Proxmox.RestProxy', {
+ extend: 'Ext.data.RestProxy',
+ alias : 'proxy.proxmox',
+
+ pageParam : null,
+ startParam: null,
+ limitParam: null,
+ groupParam: null,
+ sortParam: null,
+ filterParam: null,
+ noCache : false,
+
+ afterRequest: function(request, success) {
+ this.fireEvent('afterload', this, request, success);
+ return;
+ },
+
+ constructor: function(config) {
+
+ Ext.applyIf(config, {
+ reader: {
+ type: 'json',
+ rootProperty: config.root || 'data'
+ }
+ });
+
+ this.callParent([config]);
+ }
+});
--- /dev/null
+Ext.define('Timezone', {
+ extend: 'Ext.data.Model',
+ fields: ['zone']
+});
+
+Ext.define('Proxmox.data.TimezoneStore', {
+ extend: 'Ext.data.Store',
+ model: 'Timezone',
+ data: [
+ ['Africa/Abidjan'],
+ ['Africa/Accra'],
+ ['Africa/Addis_Ababa'],
+ ['Africa/Algiers'],
+ ['Africa/Asmara'],
+ ['Africa/Bamako'],
+ ['Africa/Bangui'],
+ ['Africa/Banjul'],
+ ['Africa/Bissau'],
+ ['Africa/Blantyre'],
+ ['Africa/Brazzaville'],
+ ['Africa/Bujumbura'],
+ ['Africa/Cairo'],
+ ['Africa/Casablanca'],
+ ['Africa/Ceuta'],
+ ['Africa/Conakry'],
+ ['Africa/Dakar'],
+ ['Africa/Dar_es_Salaam'],
+ ['Africa/Djibouti'],
+ ['Africa/Douala'],
+ ['Africa/El_Aaiun'],
+ ['Africa/Freetown'],
+ ['Africa/Gaborone'],
+ ['Africa/Harare'],
+ ['Africa/Johannesburg'],
+ ['Africa/Kampala'],
+ ['Africa/Khartoum'],
+ ['Africa/Kigali'],
+ ['Africa/Kinshasa'],
+ ['Africa/Lagos'],
+ ['Africa/Libreville'],
+ ['Africa/Lome'],
+ ['Africa/Luanda'],
+ ['Africa/Lubumbashi'],
+ ['Africa/Lusaka'],
+ ['Africa/Malabo'],
+ ['Africa/Maputo'],
+ ['Africa/Maseru'],
+ ['Africa/Mbabane'],
+ ['Africa/Mogadishu'],
+ ['Africa/Monrovia'],
+ ['Africa/Nairobi'],
+ ['Africa/Ndjamena'],
+ ['Africa/Niamey'],
+ ['Africa/Nouakchott'],
+ ['Africa/Ouagadougou'],
+ ['Africa/Porto-Novo'],
+ ['Africa/Sao_Tome'],
+ ['Africa/Tripoli'],
+ ['Africa/Tunis'],
+ ['Africa/Windhoek'],
+ ['America/Adak'],
+ ['America/Anchorage'],
+ ['America/Anguilla'],
+ ['America/Antigua'],
+ ['America/Araguaina'],
+ ['America/Argentina/Buenos_Aires'],
+ ['America/Argentina/Catamarca'],
+ ['America/Argentina/Cordoba'],
+ ['America/Argentina/Jujuy'],
+ ['America/Argentina/La_Rioja'],
+ ['America/Argentina/Mendoza'],
+ ['America/Argentina/Rio_Gallegos'],
+ ['America/Argentina/Salta'],
+ ['America/Argentina/San_Juan'],
+ ['America/Argentina/San_Luis'],
+ ['America/Argentina/Tucuman'],
+ ['America/Argentina/Ushuaia'],
+ ['America/Aruba'],
+ ['America/Asuncion'],
+ ['America/Atikokan'],
+ ['America/Bahia'],
+ ['America/Bahia_Banderas'],
+ ['America/Barbados'],
+ ['America/Belem'],
+ ['America/Belize'],
+ ['America/Blanc-Sablon'],
+ ['America/Boa_Vista'],
+ ['America/Bogota'],
+ ['America/Boise'],
+ ['America/Cambridge_Bay'],
+ ['America/Campo_Grande'],
+ ['America/Cancun'],
+ ['America/Caracas'],
+ ['America/Cayenne'],
+ ['America/Cayman'],
+ ['America/Chicago'],
+ ['America/Chihuahua'],
+ ['America/Costa_Rica'],
+ ['America/Cuiaba'],
+ ['America/Curacao'],
+ ['America/Danmarkshavn'],
+ ['America/Dawson'],
+ ['America/Dawson_Creek'],
+ ['America/Denver'],
+ ['America/Detroit'],
+ ['America/Dominica'],
+ ['America/Edmonton'],
+ ['America/Eirunepe'],
+ ['America/El_Salvador'],
+ ['America/Fortaleza'],
+ ['America/Glace_Bay'],
+ ['America/Godthab'],
+ ['America/Goose_Bay'],
+ ['America/Grand_Turk'],
+ ['America/Grenada'],
+ ['America/Guadeloupe'],
+ ['America/Guatemala'],
+ ['America/Guayaquil'],
+ ['America/Guyana'],
+ ['America/Halifax'],
+ ['America/Havana'],
+ ['America/Hermosillo'],
+ ['America/Indiana/Indianapolis'],
+ ['America/Indiana/Knox'],
+ ['America/Indiana/Marengo'],
+ ['America/Indiana/Petersburg'],
+ ['America/Indiana/Tell_City'],
+ ['America/Indiana/Vevay'],
+ ['America/Indiana/Vincennes'],
+ ['America/Indiana/Winamac'],
+ ['America/Inuvik'],
+ ['America/Iqaluit'],
+ ['America/Jamaica'],
+ ['America/Juneau'],
+ ['America/Kentucky/Louisville'],
+ ['America/Kentucky/Monticello'],
+ ['America/La_Paz'],
+ ['America/Lima'],
+ ['America/Los_Angeles'],
+ ['America/Maceio'],
+ ['America/Managua'],
+ ['America/Manaus'],
+ ['America/Marigot'],
+ ['America/Martinique'],
+ ['America/Matamoros'],
+ ['America/Mazatlan'],
+ ['America/Menominee'],
+ ['America/Merida'],
+ ['America/Mexico_City'],
+ ['America/Miquelon'],
+ ['America/Moncton'],
+ ['America/Monterrey'],
+ ['America/Montevideo'],
+ ['America/Montreal'],
+ ['America/Montserrat'],
+ ['America/Nassau'],
+ ['America/New_York'],
+ ['America/Nipigon'],
+ ['America/Nome'],
+ ['America/Noronha'],
+ ['America/North_Dakota/Center'],
+ ['America/North_Dakota/New_Salem'],
+ ['America/Ojinaga'],
+ ['America/Panama'],
+ ['America/Pangnirtung'],
+ ['America/Paramaribo'],
+ ['America/Phoenix'],
+ ['America/Port-au-Prince'],
+ ['America/Port_of_Spain'],
+ ['America/Porto_Velho'],
+ ['America/Puerto_Rico'],
+ ['America/Rainy_River'],
+ ['America/Rankin_Inlet'],
+ ['America/Recife'],
+ ['America/Regina'],
+ ['America/Resolute'],
+ ['America/Rio_Branco'],
+ ['America/Santa_Isabel'],
+ ['America/Santarem'],
+ ['America/Santiago'],
+ ['America/Santo_Domingo'],
+ ['America/Sao_Paulo'],
+ ['America/Scoresbysund'],
+ ['America/Shiprock'],
+ ['America/St_Barthelemy'],
+ ['America/St_Johns'],
+ ['America/St_Kitts'],
+ ['America/St_Lucia'],
+ ['America/St_Thomas'],
+ ['America/St_Vincent'],
+ ['America/Swift_Current'],
+ ['America/Tegucigalpa'],
+ ['America/Thule'],
+ ['America/Thunder_Bay'],
+ ['America/Tijuana'],
+ ['America/Toronto'],
+ ['America/Tortola'],
+ ['America/Vancouver'],
+ ['America/Whitehorse'],
+ ['America/Winnipeg'],
+ ['America/Yakutat'],
+ ['America/Yellowknife'],
+ ['Antarctica/Casey'],
+ ['Antarctica/Davis'],
+ ['Antarctica/DumontDUrville'],
+ ['Antarctica/Macquarie'],
+ ['Antarctica/Mawson'],
+ ['Antarctica/McMurdo'],
+ ['Antarctica/Palmer'],
+ ['Antarctica/Rothera'],
+ ['Antarctica/South_Pole'],
+ ['Antarctica/Syowa'],
+ ['Antarctica/Vostok'],
+ ['Arctic/Longyearbyen'],
+ ['Asia/Aden'],
+ ['Asia/Almaty'],
+ ['Asia/Amman'],
+ ['Asia/Anadyr'],
+ ['Asia/Aqtau'],
+ ['Asia/Aqtobe'],
+ ['Asia/Ashgabat'],
+ ['Asia/Baghdad'],
+ ['Asia/Bahrain'],
+ ['Asia/Baku'],
+ ['Asia/Bangkok'],
+ ['Asia/Beirut'],
+ ['Asia/Bishkek'],
+ ['Asia/Brunei'],
+ ['Asia/Choibalsan'],
+ ['Asia/Chongqing'],
+ ['Asia/Colombo'],
+ ['Asia/Damascus'],
+ ['Asia/Dhaka'],
+ ['Asia/Dili'],
+ ['Asia/Dubai'],
+ ['Asia/Dushanbe'],
+ ['Asia/Gaza'],
+ ['Asia/Harbin'],
+ ['Asia/Ho_Chi_Minh'],
+ ['Asia/Hong_Kong'],
+ ['Asia/Hovd'],
+ ['Asia/Irkutsk'],
+ ['Asia/Jakarta'],
+ ['Asia/Jayapura'],
+ ['Asia/Jerusalem'],
+ ['Asia/Kabul'],
+ ['Asia/Kamchatka'],
+ ['Asia/Karachi'],
+ ['Asia/Kashgar'],
+ ['Asia/Kathmandu'],
+ ['Asia/Kolkata'],
+ ['Asia/Krasnoyarsk'],
+ ['Asia/Kuala_Lumpur'],
+ ['Asia/Kuching'],
+ ['Asia/Kuwait'],
+ ['Asia/Macau'],
+ ['Asia/Magadan'],
+ ['Asia/Makassar'],
+ ['Asia/Manila'],
+ ['Asia/Muscat'],
+ ['Asia/Nicosia'],
+ ['Asia/Novokuznetsk'],
+ ['Asia/Novosibirsk'],
+ ['Asia/Omsk'],
+ ['Asia/Oral'],
+ ['Asia/Phnom_Penh'],
+ ['Asia/Pontianak'],
+ ['Asia/Pyongyang'],
+ ['Asia/Qatar'],
+ ['Asia/Qyzylorda'],
+ ['Asia/Rangoon'],
+ ['Asia/Riyadh'],
+ ['Asia/Sakhalin'],
+ ['Asia/Samarkand'],
+ ['Asia/Seoul'],
+ ['Asia/Shanghai'],
+ ['Asia/Singapore'],
+ ['Asia/Taipei'],
+ ['Asia/Tashkent'],
+ ['Asia/Tbilisi'],
+ ['Asia/Tehran'],
+ ['Asia/Thimphu'],
+ ['Asia/Tokyo'],
+ ['Asia/Ulaanbaatar'],
+ ['Asia/Urumqi'],
+ ['Asia/Vientiane'],
+ ['Asia/Vladivostok'],
+ ['Asia/Yakutsk'],
+ ['Asia/Yekaterinburg'],
+ ['Asia/Yerevan'],
+ ['Atlantic/Azores'],
+ ['Atlantic/Bermuda'],
+ ['Atlantic/Canary'],
+ ['Atlantic/Cape_Verde'],
+ ['Atlantic/Faroe'],
+ ['Atlantic/Madeira'],
+ ['Atlantic/Reykjavik'],
+ ['Atlantic/South_Georgia'],
+ ['Atlantic/St_Helena'],
+ ['Atlantic/Stanley'],
+ ['Australia/Adelaide'],
+ ['Australia/Brisbane'],
+ ['Australia/Broken_Hill'],
+ ['Australia/Currie'],
+ ['Australia/Darwin'],
+ ['Australia/Eucla'],
+ ['Australia/Hobart'],
+ ['Australia/Lindeman'],
+ ['Australia/Lord_Howe'],
+ ['Australia/Melbourne'],
+ ['Australia/Perth'],
+ ['Australia/Sydney'],
+ ['Europe/Amsterdam'],
+ ['Europe/Andorra'],
+ ['Europe/Athens'],
+ ['Europe/Belgrade'],
+ ['Europe/Berlin'],
+ ['Europe/Bratislava'],
+ ['Europe/Brussels'],
+ ['Europe/Bucharest'],
+ ['Europe/Budapest'],
+ ['Europe/Chisinau'],
+ ['Europe/Copenhagen'],
+ ['Europe/Dublin'],
+ ['Europe/Gibraltar'],
+ ['Europe/Guernsey'],
+ ['Europe/Helsinki'],
+ ['Europe/Isle_of_Man'],
+ ['Europe/Istanbul'],
+ ['Europe/Jersey'],
+ ['Europe/Kaliningrad'],
+ ['Europe/Kiev'],
+ ['Europe/Lisbon'],
+ ['Europe/Ljubljana'],
+ ['Europe/London'],
+ ['Europe/Luxembourg'],
+ ['Europe/Madrid'],
+ ['Europe/Malta'],
+ ['Europe/Mariehamn'],
+ ['Europe/Minsk'],
+ ['Europe/Monaco'],
+ ['Europe/Moscow'],
+ ['Europe/Oslo'],
+ ['Europe/Paris'],
+ ['Europe/Podgorica'],
+ ['Europe/Prague'],
+ ['Europe/Riga'],
+ ['Europe/Rome'],
+ ['Europe/Samara'],
+ ['Europe/San_Marino'],
+ ['Europe/Sarajevo'],
+ ['Europe/Simferopol'],
+ ['Europe/Skopje'],
+ ['Europe/Sofia'],
+ ['Europe/Stockholm'],
+ ['Europe/Tallinn'],
+ ['Europe/Tirane'],
+ ['Europe/Uzhgorod'],
+ ['Europe/Vaduz'],
+ ['Europe/Vatican'],
+ ['Europe/Vienna'],
+ ['Europe/Vilnius'],
+ ['Europe/Volgograd'],
+ ['Europe/Warsaw'],
+ ['Europe/Zagreb'],
+ ['Europe/Zaporozhye'],
+ ['Europe/Zurich'],
+ ['Indian/Antananarivo'],
+ ['Indian/Chagos'],
+ ['Indian/Christmas'],
+ ['Indian/Cocos'],
+ ['Indian/Comoro'],
+ ['Indian/Kerguelen'],
+ ['Indian/Mahe'],
+ ['Indian/Maldives'],
+ ['Indian/Mauritius'],
+ ['Indian/Mayotte'],
+ ['Indian/Reunion'],
+ ['Pacific/Apia'],
+ ['Pacific/Auckland'],
+ ['Pacific/Chatham'],
+ ['Pacific/Chuuk'],
+ ['Pacific/Easter'],
+ ['Pacific/Efate'],
+ ['Pacific/Enderbury'],
+ ['Pacific/Fakaofo'],
+ ['Pacific/Fiji'],
+ ['Pacific/Funafuti'],
+ ['Pacific/Galapagos'],
+ ['Pacific/Gambier'],
+ ['Pacific/Guadalcanal'],
+ ['Pacific/Guam'],
+ ['Pacific/Honolulu'],
+ ['Pacific/Johnston'],
+ ['Pacific/Kiritimati'],
+ ['Pacific/Kosrae'],
+ ['Pacific/Kwajalein'],
+ ['Pacific/Majuro'],
+ ['Pacific/Marquesas'],
+ ['Pacific/Midway'],
+ ['Pacific/Nauru'],
+ ['Pacific/Niue'],
+ ['Pacific/Norfolk'],
+ ['Pacific/Noumea'],
+ ['Pacific/Pago_Pago'],
+ ['Pacific/Palau'],
+ ['Pacific/Pitcairn'],
+ ['Pacific/Pohnpei'],
+ ['Pacific/Port_Moresby'],
+ ['Pacific/Rarotonga'],
+ ['Pacific/Saipan'],
+ ['Pacific/Tahiti'],
+ ['Pacific/Tarawa'],
+ ['Pacific/Tongatapu'],
+ ['Pacific/Wake'],
+ ['Pacific/Wallis']
+ ]
+});
--- /dev/null
+// Serialize load (avoid too many parallel connections)
+Ext.define('Proxmox.data.UpdateQueue', {
+ singleton: true,
+
+ constructor : function(){
+ var me = this;
+
+ var queue = [];
+ var queue_idx = {};
+
+ var idle = true;
+
+ var start_update = function() {
+ if (!idle) {
+ return;
+ }
+
+ var storeid = queue.shift();
+ if (!storeid) {
+ return;
+ }
+ var info = queue_idx[storeid];
+ queue_idx[storeid] = null;
+
+ info.updatestart = new Date();
+
+ idle = false;
+ info.store.load({
+ callback: function(records, operation, success) {
+ idle = true;
+ if (info.callback) {
+ var runtime = (new Date()).getTime() - info.updatestart.getTime();
+ info.callback(runtime, success);
+ }
+ start_update();
+ }
+ });
+ };
+
+ Ext.apply(me, {
+ queue: function(store, cb) {
+ var storeid = store.storeid;
+ if (!storeid) {
+ throw "unable to queue store without storeid";
+ }
+ if (!queue_idx[storeid]) {
+ queue_idx[storeid] = {
+ store: store,
+ callback: cb
+ };
+ queue.push(storeid);
+ }
+ start_update();
+ },
+ unqueue: function(store) {
+ var storeid = store.storeid;
+ if (!storeid) {
+ throw "unabel to unqueue store without storeid";
+ }
+ if (queue_idx[storeid]) {
+ Ext.Array.remove(queue,storeid);
+ queue_idx[storeid] = null;
+ }
+ }
+ });
+ }
+});
--- /dev/null
+/* Extends the Ext.data.Store type
+ * with startUpdate() and stopUpdate() methods
+ * to refresh the store data in the background
+ * Components using this store directly will flicker
+ * due to the redisplay of the element ater 'config.interval' ms
+ *
+ * Note that you have to call yourself startUpdate() for the background load
+ * to begin
+ */
+Ext.define('Proxmox.data.UpdateStore', {
+ extend: 'Ext.data.Store',
+
+ isStopped: true,
+
+ constructor: function(config) {
+ var me = this;
+
+ config = config || {};
+
+ if (!config.interval) {
+ config.interval = 3000;
+ }
+
+ if (!config.storeid) {
+ throw "no storeid specified";
+ }
+
+ var load_task = new Ext.util.DelayedTask();
+
+ var run_load_task = function() {
+ if (me.isStopped) {
+ return;
+ }
+
+ if (Proxmox.Utils.authOK()) {
+ Proxmox.data.UpdateQueue.queue(me, function(runtime, success) {
+ var interval = config.interval + runtime*2;
+ load_task.delay(interval, run_load_task);
+ });
+ } else {
+ load_task.delay(200, run_load_task);
+ }
+ };
+
+ Ext.apply(config, {
+ startUpdate: function() {
+ me.isStopped = false;
+ run_load_task();
+ },
+ stopUpdate: function() {
+ me.isStopped = true;
+ load_task.cancel();
+ Proxmox.data.UpdateQueue.unqueue(me);
+ }
+ });
+
+ me.callParent([config]);
+
+ me.on('destroy', function() {
+ load_task.cancel();
+ Proxmox.data.UpdateQueue.unqueue(me);
+ });
+ }
+});
--- /dev/null
+/* A reader to store a single JSON Object (hash) into a storage.
+ * Also accepts an array containing a single hash.
+ *
+ * So it can read:
+ *
+ * example1: {data1: "xyz", data2: "abc"}
+ * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}]
+ *
+ * example2: [ {data1: "xyz", data2: "abc"} ]
+ * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}]
+ *
+ * If you set 'readArray', the reader expexts the object as array:
+ *
+ * example3: [ { key: "data1", value: "xyz", p2: "cde" }, { key: "data2", value: "abc", p2: "efg" }]
+ * returns [{key: "data1", value: "xyz", p2: "cde}, {key: "data2", value: "abc", p2: "efg"}]
+ *
+ * Note: The records can contain additional properties (like 'p2' above) when you use 'readArray'
+ *
+ * Additional feature: specify allowed properties with default values with 'rows' object
+ *
+ * var rows = {
+ * memory: {
+ * required: true,
+ * defaultValue: 512
+ * }
+ * }
+ *
+ */
+
+Ext.define('Proxmox.data.reader.JsonObject', {
+ extend: 'Ext.data.reader.Json',
+ alias : 'reader.jsonobject',
+
+ readArray: false,
+
+ rows: undefined,
+
+ constructor: function(config) {
+ var me = this;
+
+ Ext.apply(me, config || {});
+
+ me.callParent([config]);
+ },
+
+ getResponseData: function(response) {
+ var me = this;
+
+ var data = [];
+ try {
+ var result = Ext.decode(response.responseText);
+ // get our data items inside the server response
+ var root = result[me.getRootProperty()];
+
+ if (me.readArray) {
+
+ var rec_hash = {};
+ Ext.Array.each(root, function(rec) {
+ if (Ext.isDefined(rec.key)) {
+ rec_hash[rec.key] = rec;
+ }
+ });
+
+ if (me.rows) {
+ Ext.Object.each(me.rows, function(key, rowdef) {
+ var rec = rec_hash[key];
+ if (Ext.isDefined(rec)) {
+ if (!Ext.isDefined(rec.value)) {
+ rec.value = rowdef.defaultValue;
+ }
+ data.push(rec);
+ } else if (Ext.isDefined(rowdef.defaultValue)) {
+ data.push({key: key, value: rowdef.defaultValue} );
+ } else if (rowdef.required) {
+ data.push({key: key, value: undefined });
+ }
+ });
+ } else {
+ Ext.Array.each(root, function(rec) {
+ if (Ext.isDefined(rec.key)) {
+ data.push(rec);
+ }
+ });
+ }
+
+ } else {
+
+ var org_root = root;
+
+ if (Ext.isArray(org_root)) {
+ if (root.length == 1) {
+ root = org_root[0];
+ } else {
+ root = {};
+ }
+ }
+
+ if (me.rows) {
+ Ext.Object.each(me.rows, function(key, rowdef) {
+ if (Ext.isDefined(root[key])) {
+ data.push({key: key, value: root[key]});
+ } else if (Ext.isDefined(rowdef.defaultValue)) {
+ data.push({key: key, value: rowdef.defaultValue});
+ } else if (rowdef.required) {
+ data.push({key: key, value: undefined});
+ }
+ });
+ } else {
+ Ext.Object.each(root, function(key, value) {
+ data.push({key: key, value: value });
+ });
+ }
+ }
+ }
+ catch (ex) {
+ Ext.Error.raise({
+ response: response,
+ json: response.responseText,
+ parseError: ex,
+ msg: 'Unable to parse the JSON returned by the server: ' + ex.toString()
+ });
+ }
+
+ return data;
+ }
+});
+