{"id":170129,"date":"2013-03-11T13:18:03","date_gmt":"2013-03-11T09:18:03","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=170129"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=170129","title":{"rendered":"<span class=\"post_title\">\u041a\u0430\u0440\u0442\u044b \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0431\u0435\u0437 \u0441\u0435\u0442\u0438: open source \u043d\u0430\u043d\u043e\u0441\u0438\u0442 \u043e\u0442\u0432\u0435\u0442\u043d\u044b\u0439 \u0443\u0434\u0430\u0440<\/span>"},"content":{"rendered":"<div class=\"content html_format\">   \t\u041a\u0430\u043a-\u0442\u043e \u0434\u0430\u0432\u043d\u043e \u044f <a href=\"http:\/\/habrahabr.ru\/post\/141707\/\">\u043f\u0438\u0441\u0430\u043b \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0432 \u0432\u0435\u0431\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0440\u0442\u044b \u0431\u0435\u0437 \u0441\u0435\u0442\u0438<\/a> \u0438 \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0433\u0443\u0433\u043b\u043e \u043a\u0430\u0440\u0442. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u043b\u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441\u044b, \u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u043c\u043d\u043e\u044e \u043a\u043e\u0434 \u0440\u0430\u0431\u043e\u0442\u0430\u043b \u0442\u043e\u043b\u044c\u043a\u043e \u0441 <code>localstorage<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u0440\u0435\u0448\u0438\u043b \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043d\u0430 \u0441\u0432\u0435\u0442\u043b\u0443\u044e \u0441\u0442\u043e\u0440\u043e\u043d\u0443 \u0441\u0438\u043b\u044b, \u0433\u0434\u0435 \u043a\u043e\u0434 \u043e\u0442\u043a\u0440\u044b\u0442, \u043f\u0440\u043e\u0441\u0442 \u0438 \u043f\u043e\u043d\u044f\u0442\u0435\u043d.<a name=\"habracut\"><\/a><\/p>\n<h4>\u0427\u0435\u0433\u043e \u0436\u0435 \u044f \u0445\u043e\u0447\u0443?<\/h4>\n<p>  \u042f \u0445\u043e\u0447\u0443 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u0430\u0440\u0442\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0431\u0435\u0437 \u0441\u0435\u0442\u0438, \u0442\u0435 \u0432 \u043f\u0435\u0440\u0432\u044b\u0439 \u0440\u0430\u0437 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0448\u044c \u043a\u0430\u0440\u0442\u0443, \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u0448\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0449\u0438\u0435 \u0442\u0430\u0439\u043b\u044b (\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f) \u0438 \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0440\u0430\u0437 \u043a\u0430\u0440\u0442\u0430 \u0441 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u043c\u0438 \u0442\u0430\u0439\u043b\u0430\u043c\u0438 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0431\u0435\u0437 \u0441\u0435\u0442\u0438.<\/p>\n<p>  \u0412 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u043b\u0435\u0442\u0443 \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0438 \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0438 \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u0435\u0433\u0438\u043e\u043d\u0430. \u041d\u043e \u044f \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c \u0445\u043e\u0447\u0443 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434.<\/p>\n<h4>\u0427\u0442\u043e \u0436\u0435 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c?<\/h4>\n<p>  \u0412 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u0432\u0435\u0431\u0435 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u0434\u043e\u0439\u0442\u0438:<br \/>  <a href=\"http:\/\/caniuse.com\/offline-apps\">Application Cache<\/a> \u2014 \u0434\u043b\u044f \u0441\u0442\u0430\u0442\u0438\u043a\u0438, \u043d\u043e \u043d\u0435 \u0434\u043b\u044f \u0442\u0430\u0439\u043b\u043e\u0432.<br \/>  <a href=\"http:\/\/caniuse.com\/namevalue-storage\">Local Storage<\/a> \u2014 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c base64 data uri, \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u0435\u0437\u0434\u0435, \u043d\u043e \u043e\u0447\u0435\u043d\u044c \u043c\u0430\u043b\u043e \u043c\u0435\u0441\u0442\u0430.<br \/>  <a href=\"http:\/\/caniuse.com\/indexeddb\">Indexed DB<\/a> \u2014 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 base64 data uri, \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0445 \u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0445\u0440\u043e\u043c\u0435, ff, ie10.<br \/>  <a href=\"http:\/\/caniuse.com\/sql-storage\">Web SQL<\/a> \u2014 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 base64 data uri, \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e, \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d \u043a\u0430\u043a \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0439, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0445 \u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0445\u0440\u043e\u043c\u0435, \u0441\u0430\u0444\u0430\u0440\u0438, \u043e\u043f\u0435\u0440\u0435, \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0430\u043d\u0434\u0440\u043e\u0438\u0434\u0430.<br \/>  <a href=\"http:\/\/caniuse.com\/filesystem\">File Writer<\/a> \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0445\u0440\u043e\u043c.<\/p>\n<p>  \u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"http:\/\/caniuse.com\/blobbuilder\">\u0431\u043b\u043e\u0431\u044b<\/a> \u0438 <a href=\"http:\/\/caniuse.com\/bloburls\">\u0431\u043b\u043e\u0431 \u0443\u0440\u043b\u044b<\/a> \u0434\u043b\u044f \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u043c\u043e\u0433\u043e \u0442\u0430\u0439\u043b\u0430\u043c\u0438 \u043c\u0435\u0441\u0442\u0430, \u043d\u043e \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 <a href=\"http:\/\/caniuse.com\/indexeddb\">Indexed DB<\/a>. \u042d\u0442\u0443 \u0437\u0430\u0442\u0435\u044e \u044f \u043f\u043e\u043a\u0430 \u043e\u0441\u0442\u0430\u0432\u043b\u044e.<\/p>\n<p>  \u0418\u0442\u0430\u043a, \u0435\u0441\u043b\u0438 \u043a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c <a href=\"http:\/\/caniuse.com\/offline-apps\">Application Cache<\/a>, <a href=\"http:\/\/caniuse.com\/indexeddb\">Indexed DB<\/a> \u0438 <a href=\"http:\/\/caniuse.com\/sql-storage\">Web SQL<\/a>, \u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0448\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0442\u0430\u0439\u043b\u043e\u0432 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u0443\u044e \u0434\u043b\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u0445, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445.<\/p>\n<h4>\u0422\u0435\u043e\u0440\u0438\u044f<\/h4>\n<p>  \u0412 \u0442\u0435\u043e\u0440\u0438\u0438 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e:  <\/p>\n<ol>\n<li>\u0432\u0437\u044f\u0442\u044c API;<\/li>\n<li>\u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u0441\u044e \u0441\u0442\u0430\u0442\u0438\u043a\u0443 \u0432 Application Cache;<\/li>\n<li>\u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u043b\u043e\u0439 \u0442\u0430\u0439\u043b\u043e\u0432 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u043b \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043d\u0430\u0448\u0438\u0445 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449;<\/li>\n<li>\u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0442\u0430\u0439\u043b\u043e\u0432 \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430.<\/li>\n<\/ol>\n<p>  <\/p>\n<h4>\u0425\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435<\/h4>\n<p>  \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0443\u0435\u043c key-value \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0441 \u0431\u0430\u0437\u043e\u0432\u044b\u043c\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438 add, delete, get \u0434\u043b\u044f Indexed DB \u0438 Web SQL. \u0417\u0434\u0435\u0441\u044c \u0435\u0441\u0442\u044c \u043e\u0434\u043d\u0430 \u043c\u0430\u0433\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f <code>emr.fire('storageLoaded', storage);<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043f\u0440\u043e\u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0438 \u0433\u043e\u0442\u043e\u0432\u043e \u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e, \u0447\u0442\u043e\u0431\u044b \u043a\u0430\u0440\u0442\u0430 \u043d\u0435 \u043f\u0430\u0434\u0430\u043b\u0430 \u043f\u0440\u0438 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0438 \u043a \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0443.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Indexed DB<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">var getIndexedDBStorage = function () {     var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;      var IndexedDBImpl = function () {         var self = this;         var db = null;         var request = indexedDB.open('TileStorage');          request.onsuccess = function() {             db = this.result;             emr.fire('storageLoaded', self);         };          request.onerror = function (error) {             console.log(error);         };          request.onupgradeneeded = function () {             var store = this.result.createObjectStore('tile', { keyPath: 'key'});             store.createIndex('key', 'key', { unique: true });         };          this.add = function (key, value) {             var transaction = db.transaction(['tile'], 'readwrite');             var objectStore = transaction.objectStore('tile');             objectStore.put({key: key, value: value});         };          this.delete = function (key) {             var transaction = db.transaction(['tile'], 'readwrite');             var objectStore = transaction.objectStore('tile');             objectStore.delete(key);         };          this.get = function (key, successCallback, errorCallback) {             var transaction = db.transaction(['tile'], 'readonly');             var objectStore = transaction.objectStore('tile');             var result = objectStore.get(key);             result.onsuccess = function () {                 successCallback(this.result ? this.result.value : undefined);             };             result.onerror = errorCallback;         };     };      return indexedDB ? new IndexedDBImpl() : null; }; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Web SQL<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">var getWebSqlStorage = function () {     var openDatabase = window.openDatabase;      var WebSqlImpl = function () {         var self = this;         var db = openDatabase('TileStorage', '1.0', 'Tile Storage', 5 * 1024 * 1024);         db.transaction(function (tx) {             tx.executeSql('CREATE TABLE IF NOT EXISTS tile (key TEXT PRIMARY KEY, value TEXT)', [], function () {                 emr.fire('storageLoaded', self);             });         });          this.add = function (key, value) {             db.transaction(function (tx) {                 tx.executeSql('INSERT INTO tile (key, value) VALUES (?, ?)', [key, value]);             });         };          this.delete = function (key) {             db.transaction(function (tx) {                 tx.executeSql('DELETE FROM tile WHERE key = ?', [key]);             });         };          this.get = function (key, successCallback, errorCallback) {             db.transaction(function (tx) {                 tx.executeSql('SELECT value FROM tile WHERE key = ?', [key], function (tx, result) {                     successCallback(result.rows.length ? result.rows.item(0).value : undefined);                 }, errorCallback);             });         };     };      return openDatabase ? new WebSqlImpl() : null; }; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">var storage =  getIndexedDBStorage() || getWebSqlStorage() || null; if (!storage) {     emr.fire('storageLoaded', null); } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043e\u0447\u0435\u043d\u044c \u0441\u0445\u0435\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0439, \u0434\u0443\u043c\u0430\u044e \u0437\u0434\u0435\u0441\u044c \u0435\u0441\u0442\u044c \u043d\u0430\u0434 \u0447\u0435\u043c \u043f\u043e\u0434\u0443\u043c\u0430\u0442\u044c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043a\u0430\u0440\u0442\u044b, \u043f\u043e\u043a\u0430 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435; \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0442\u044c \u043a\u0430\u043a\u0438\u0435 \u0442\u0430\u0439\u043b\u044b \u0435\u0441\u0442\u044c \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0431\u0435\u0437 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a API; \u043f\u043e\u043f\u044b\u0442\u0430\u0442\u044c\u0441\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432 \u043e\u0434\u043d\u0443 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e, \u0447\u0442\u043e\u0431\u044b \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043d\u0430 \u0434\u0438\u0441\u043a; \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u043b\u043e\u0431\u044b \u0442\u0430\u043c \u0433\u0434\u0435 \u043e\u043d\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f Indexed DB \u0432 \u0441\u0442\u0430\u0440\u044b\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u0445 \u0431\u0443\u0434\u0435\u0442 \u043f\u0430\u0434\u0430\u0442\u044c, \u0442\u043a \u0432 \u043d\u0438\u0445 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0441\u043e\u0431\u044b\u0442\u0438\u0435 <code>onupgradeneeded<\/code>.<\/p>\n<h4>IMG to data URI &#038; CORS<\/h4>\n<p>  \u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0442\u0430\u0439\u043b\u044b \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0432 data URI, \u0442\u043e \u0435\u0441\u0442\u044c base64 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f canvas \u0438 \u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 <code>toDataURL<\/code> \u0438\u043b\u0438 <code>getImageData<\/code>:<\/p>\n<pre><code class=\"javascript\">_imageToDataUri: function (image) {     var canvas = window.document.createElement('canvas');     canvas.width = image.width;     canvas.height = image.height;      var context = canvas.getContext('2d');     context.drawImage(image, 0, 0);      return canvas.toDataURL('image\/png'); } <\/code><\/pre>\n<p>  \u0422\u0430\u043a \u043a\u0430\u043a html \u044d\u043b\u0435\u043c\u0435\u043d\u0442 img \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043b\u044e\u0431\u043e\u0439 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u0440\u0435\u0441\u0443\u0440\u0441, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0438 \u043d\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 \u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u0442\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u044d\u0442\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u0443\u0433\u0440\u043e\u0437\u0443 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u044e\u0449\u0438\u0435 <code>Access-Control-Allow-Origing<\/code> \u0434\u043b\u044f \u0412\u0430\u0448\u0435\u0433\u043e \u0434\u043e\u043c\u0435\u043d\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u043b\u044c\u0437\u044f. \u0411\u043b\u0430\u0433\u043e \u0442\u0430\u0439\u043b\u044b mapnik \u0438\u043b\u0438 \u0436\u0435 tile.openstreetmap.org \u0438\u043c\u0435\u044e\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a <code>Access-Control-Allow-Origing: *<\/code>, \u043d\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0444\u043b\u0430\u0433 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 <code>img.crossOrigin<\/code> \u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>Anonymous<\/code>.<\/p>\n<p>  \u0420\u0430\u0431\u043e\u0442\u0430 CORS \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u043e \u0432\u0441\u0435\u0445 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u0445 \u043d\u0435 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u043e\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u043b\u044f \u0441\u0432\u043e\u0435\u0433\u043e \u0441\u0430\u0439\u0442\u0430 \u043f\u0440\u043e\u043a\u0441\u0438 \u043d\u0430 \u0441\u0432\u043e\u0435\u043c \u0434\u043e\u043c\u0435\u043d\u0435 \u0438\u043b\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 CORS \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f Phoengap \u0430\u0434\u0435\u043f\u0442\u043e\u0432. \u041b\u0438\u0447\u043d\u043e \u0443 \u043c\u0435\u043d\u044f \u0434\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0435 \u0432\u0437\u043b\u0435\u0442\u0435\u043b \u0432 \u0430\u043d\u0434\u0440\u043e\u0438\u0434\u043e\u0432\u0441\u043a\u043e\u043c \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e (androin 4.0.4 sony xperia active), \u0430 \u0432 \u043e\u043f\u0435\u0440\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0430\u0439\u043b\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u043b\u0438\u0441\u044c \u0441\u0442\u0440\u0430\u043d\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c (\u0441\u0440\u0430\u0432\u043d\u0438 <a href=\"http:\/\/habrastorage.org\/storage2\/d7a\/129\/0b9\/d7a1290b9987e3703ce7f4c3f10d573e.png\">\u0447\u0442\u043e \u0438\u043d\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f<\/a> \u0438 <a href=\"http:\/\/tile.openstreetmap.org\/14\/9445\/5267.png\">\u0442\u043e \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435<\/a>, \u043d\u043e \u044d\u0442\u043e \u043f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 \u0431\u0430\u0433 \u043e\u043f\u0435\u0440\u044b).<\/p>\n<p>  \u0417\u0434\u0435\u0441\u044c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"http:\/\/caniuse.com\/webworkers\"><code>WebWorkers<\/code><\/a> + <code>AJAX<\/code> \u0432\u043c\u0435\u0441\u0442\u043e <code>canvas<\/code>.<\/p>\n<h4>Leaflet<\/h4>\n<p>  \u0418\u0442\u0430\u043a \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0435 JS API \u043a\u0430\u0440\u0442 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u043a\u043e\u0434\u043e\u043c, \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u043e\u0432 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <a href=\"http:\/\/leafletjs.com\/\">Leaflet<\/a>.<\/p>\n<p>  \u041d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0432 <a href=\"https:\/\/github.com\/CloudMade\/Leaflet\/tree\/333899ca518b6ad7080e7e9ddb0492d4ba4c5d51\/src\/layer\/tile\/TileLayer.js#L477\">\u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438<\/a> \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043c\u0435\u0442\u043e\u0434 \u0442\u0430\u0439\u043b\u043e\u0432\u043e\u0433\u043e \u0441\u043b\u043e\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u0435 <code>src<\/code> \u0434\u043b\u044f \u0442\u0430\u0439\u043b\u043e\u0432: <\/p>\n<pre><code class=\"javascript\">_loadTile: function (tile, tilePoint) {     tile._layer = this;     tile.onload = this._tileOnLoad;     tile.onerror = this._tileOnError;      tile.src = this.getTileUrl(tilePoint); } <\/code><\/pre>\n<p>  \u0422\u043e \u0435\u0441\u0442\u044c \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043a\u043b\u0430\u0441\u0441 \u0438 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 <code>src<\/code> \u0438\u0437 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430, \u0442\u043e \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0442\u043e \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e. \u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435, \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u0431\u044b\u043b\u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u0438\u0437 \u0441\u0435\u0442\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0435 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u043b\u044f Leaflet<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">var StorageTileLayer = L.TileLayer.extend({     _imageToDataUri: function (image) {         var canvas = window.document.createElement('canvas');         canvas.width = image.width;         canvas.height = image.height;          var context = canvas.getContext('2d');         context.drawImage(image, 0, 0);          return canvas.toDataURL('image\/png');     },      _tileOnLoadWithCache: function () {         var storage = this._layer.options.storage;         if (storage) {             storage.add(this._storageKey, this._layer._imageToDataUri(this));         }         L.TileLayer.prototype._tileOnLoad.apply(this, arguments);     },      _setUpTile: function (tile, key, value, cache) {         tile._layer = this;         if (cache) {             tile._storageKey = key;             tile.onload = this._tileOnLoadWithCache;             tile.crossOrigin = 'Anonymous';         } else {             tile.onload = this._tileOnLoad;         }         tile.onerror = this._tileOnError;         tile.src = value;     },      _loadTile: function (tile, tilePoint) {         this._adjustTilePoint(tilePoint);         var key = tilePoint.z + ',' + tilePoint.y + ',' + tilePoint.x;          var self = this;         if (this.options.storage) {             this.options.storage.get(key, function (value) {                 if (value) {                     self._setUpTile(tile, key, value, false);                 } else {                     self._setUpTile(tile, key, self.getTileUrl(tilePoint), true);                 }             }, function () {                 self._setUpTile(tile, key, self.getTileUrl(tilePoint), true);             });         } else {             self._setUpTile(tile, key, self.getTileUrl(tilePoint), false);         }     } }); <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0421\u0430\u043c\u0430 \u0436\u0435 \u043a\u0430\u0440\u0442\u0430 \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"javascript\">var map = L.map('map').setView([53.902254, 27.561850], 13); new StorageTileLayer('http:\/\/{s}.tile.osm.org\/{z}\/{x}\/{y}.png', {storage: storage}).addTo(map); <\/code><\/pre>\n<p>  \u0422\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430\u0448\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0432 Application Cache, \u0447\u0442\u043e\u0431\u044b \u043a\u0430\u0440\u0442\u0430 \u043c\u043e\u0433\u043b\u0430 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0431\u0435\u0437 \u0441\u0435\u0442\u0438 \u0441 \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u0442\u0430\u0439\u043b\u0430\u043c\u0438:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Application Cache manifest \u0434\u043b\u044f Leaflet<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>CACHE MANIFEST  NETWORK: *  CACHE: index.html style.css event.js storage.js map.js run.js  leaflet.css leaflet.js images\/layers.png images\/marker-icon.png images\/marker-icon@2x.png images\/marker-shadow.png <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <a href=\"http:\/\/tbicr.github.com\/OfflineMap\/leaflet\/index.html\">\u041f\u0440\u0438\u043c\u0435\u0440<\/a> \u0438 <a href=\"https:\/\/github.com\/tbicr\/OfflineMap\/tree\/master\/leaflet_idb_sql_site\">\u0435\u0433\u043e \u043a\u043e\u0434 \u043d\u0430 \u0433\u0438\u0442\u0445\u0430\u0431\u0435<\/a>.<\/p>\n<h4>Mapbox (modesmaps)<\/h4>\n<p>  \u0415\u0449\u0435 \u043e\u0434\u043d\u0438\u043c \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u043e\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0433\u043e JS API \u043a\u0430\u0440\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <a href=\"http:\/\/mapbox.com\/mapbox.js\">mapbox<\/a> \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043d\u0430 <a href=\"http:\/\/modestmaps.com\/\">modesmaps<\/a>.<\/p>\n<p>  \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0432 <a href=\"https:\/\/github.com\/mapbox\/mapbox.js\">\u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 mapbox<\/a> \u043c\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043c \u0434\u043b\u044f \u043d\u0430\u0441 \u043d\u0438\u0447\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a <a href=\"https:\/\/github.com\/modestmaps\/modestmaps-js\">\u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0430\u043c modestmaps<\/a>. \u041d\u0430\u0447\u043d\u0435\u043c \u0441 <a href=\"https:\/\/github.com\/modestmaps\/modestmaps-js\/tree\/1e46ba1431f9e731a00928fd57800268d72f7eb0\/src\/provider.js#L148\"><code>TemplatedLayer<\/code><\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u044b\u0447\u043d\u044b\u043c \u0441\u043b\u043e\u0435\u043c \u043a\u0430\u0440\u0442\u044b \u0441 \u0448\u0430\u0431\u043b\u043e\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u043c, \u0442\u0435 \u043a\u043e\u0434 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 \u0441\u043b\u043e\u044f:<\/p>\n<pre><code class=\"javascript\">MM.TemplatedLayer = function(template, subdomains, name) {     return new MM.Layer(new MM.Template(template, subdomains), null, name); }; <\/code><\/pre>\n<p>  \u041d\u0430\u0439\u0434\u044f <a href=\"https:\/\/github.com\/modestmaps\/modestmaps-js\/tree\/1e46ba1431f9e731a00928fd57800268d72f7eb0\/src\/layer.js#L176\">\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0432 \u0441\u043b\u043e\u0435 \u043a\u0430\u0440\u0442\u044b<\/a> \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043d\u0430\u0448 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u043c\u043e\u0436\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043b\u0438\u0431\u043e URL \u0442\u0430\u0439\u043b\u0430, \u043b\u0438\u0431\u043e \u0433\u043e\u0442\u043e\u0432\u044b\u0439 DOM \u044d\u043b\u0435\u043c\u0435\u043d\u0442, \u043f\u0440\u0438\u0447\u0435\u043c DOM \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u0438\u0440\u0443\u0435\u0442\u0441\u044f, \u0430 URL \u0442\u0430\u0439\u043b\u0430 \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u0430\u0435\u0442\u0441\u044f \u0432 <code>requestManager<\/code>:<\/p>\n<pre><code class=\"javascript\">if (!this.requestManager.hasRequest(tile_key)) {     var tileToRequest = this.provider.getTile(tile_coord);     if (typeof tileToRequest == 'string') {         this.addTileImage(tile_key, tile_coord, tileToRequest);     } else if (tileToRequest) {         this.addTileElement(tile_key, tile_coord, tileToRequest);     } } <\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"javascript\">addTileImage: function(key, coord, url) {     this.requestManager.requestTile(key, coord, url); } <\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"javascript\">addTileElement: function(key, coordinate, element) {     element.id = key;     element.coord = coordinate.copy();     this.positionTile(element); } <\/code><\/pre>\n<p>  \u0421\u0430\u043c \u0436\u0435 <code>requestManager<\/code> \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0435 \u0441\u043b\u043e\u044f \u043a\u0430\u0440\u0442\u044b. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 DOM \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 <code>img<\/code> \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0435\u0433\u043e <code>src<\/code> \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <a href=\"https:\/\/github.com\/modestmaps\/modestmaps-js\/tree\/1e46ba1431f9e731a00928fd57800268d72f7eb0\/src\/requests.js#L153\"><code>processQueue<\/code><\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0442\u0430\u043a\u0436\u0435 \u0434\u0435\u0440\u0433\u0430\u0435\u0442\u0441\u044f \u0438\u0437 \u0441\u043b\u043e\u044f \u043a\u0430\u0440\u0442\u044b:<\/p>\n<pre><code class=\"javascript\">processQueue: function(sortFunc) {     if (sortFunc && this.requestQueue.length &gt; 8) {         this.requestQueue.sort(sortFunc);     }     while (this.openRequestCount &lt; this.maxOpenRequests && this.requestQueue.length &gt; 0) {         var request = this.requestQueue.pop();         if (request) {             this.openRequestCount++;             var img = document.createElement('img');             img.id = request.id;             img.style.position = 'absolute';             img.coord = request.coord;             this.loadingBay.appendChild(img);             img.onload = img.onerror = this.getLoadComplete();             img.src = request.url;             request = request.id = request.coord = request.url = null;         }     } } <\/code><\/pre>\n<p>  \u0422\u043e \u0435\u0441\u0442\u044c \u0435\u0441\u043b\u0438 \u043c\u044b \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u043b\u044f mapbox (modestmaps)<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">var StorageRequestManager = function (storage) {     MM.RequestManager.apply(this, []);     this._storage = storage; };  StorageRequestManager.prototype._imageToDataUri = function (image) {     var canvas = window.document.createElement('canvas');     canvas.width = image.width;     canvas.height = image.height;      var context = canvas.getContext('2d');     context.drawImage(image, 0, 0);      return canvas.toDataURL('image\/png'); };  StorageRequestManager.prototype._createTileImage = function (id, coord, value, cache) {     var img = window.document.createElement('img');     img.id = id;     img.style.position = 'absolute';     img.coord = coord;     this.loadingBay.appendChild(img);     if (cache) {         img.onload = this.getLoadCompleteWithCache();         img.crossOrigin = 'Anonymous';     } else {         img.onload = this.getLoadComplete();     }     img.onerror = this.getLoadComplete();     img.src = value; };  StorageRequestManager.prototype._loadTile = function (id, coord, url) {     var self = this;     if (this._storage) {         this._storage.get(id, function (value) {             if (value) {                 self._createTileImage(id, coord, value, false);             } else {                 self._createTileImage(id, coord, url, true);             }         }, function () {             self._createTileImage(id, coord, url, true);         });     } else {         self._createTileImage(id, coord, url, false);     } };  StorageRequestManager.prototype.processQueue = function (sortFunc) {     if (sortFunc && this.requestQueue.length &gt; 8) {         this.requestQueue.sort(sortFunc);     }     while (this.openRequestCount &lt; this.maxOpenRequests && this.requestQueue.length &gt; 0) {         var request = this.requestQueue.pop();         if (request) {             this.openRequestCount++;             this._loadTile(request.id, request.coord, request.url);             request = request.id = request.coord = request.url = null;         }     } };  StorageRequestManager.prototype.getLoadCompleteWithCache = function () {     if (!this._loadComplete) {         var theManager = this;         this._loadComplete = function(e) {             e = e || window.event;              var img = e.srcElement || e.target;             img.onload = img.onerror = null;              if (theManager._storage) {                 theManager._storage.add(this.id, theManager._imageToDataUri(this));             }              theManager.loadingBay.removeChild(img);             theManager.openRequestCount--;             delete theManager.requestsById[img.id];              if (e.type === 'load' && (img.complete ||                 (img.readyState && img.readyState === 'complete'))) {                 theManager.dispatchCallback('requestcomplete', img);             } else {                 theManager.dispatchCallback('requesterror', {                     element: img,                     url: ('' + img.src)                 });                 img.src = null;             }              setTimeout(theManager.getProcessQueue(), 0);         };     }     return this._loadComplete; };  MM.extend(StorageRequestManager, MM.RequestManager);  var StorageLayer = function(provider, parent, name, storage) {     this.parent = parent || document.createElement('div');     this.parent.style.cssText = 'position: absolute; top: 0px; left: 0px;' +         'width: 100%; height: 100%; margin: 0; padding: 0; z-index: 0';     this.name = name;     this.levels = {};     this.requestManager = new StorageRequestManager(storage);     this.requestManager.addCallback('requestcomplete', this.getTileComplete());     this.requestManager.addCallback('requesterror', this.getTileError());     if (provider) {         this.setProvider(provider);     } };  MM.extend(StorageLayer, MM.Layer);  var StorageTemplatedLayer = function(template, subdomains, name, storage) {     return new StorageLayer(new MM.Template(template, subdomains), null, name, storage); }; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0421\u0430\u043c\u0430 \u0436\u0435 \u043a\u0430\u0440\u0442\u0430 \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"javascript\">var map = mapbox.map('map'); map.addLayer(new StorageTemplatedLayer('http:\/\/{S}.tile.osm.org\/{Z}\/{X}\/{Y}.png', ['a', 'b', 'c'], undefined, storage)); map.ui.zoomer.add(); map.ui.zoombox.add(); map.centerzoom({lat: 53.902254, lon: 27.561850}, 13); <\/code><\/pre>\n<p>  \u0422\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430\u0448\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0432 Application Cache, \u0447\u0442\u043e\u0431\u044b \u043a\u0430\u0440\u0442\u0430 \u043c\u043e\u0433\u043b\u0430 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0431\u0435\u0437 \u0441\u0435\u0442\u0438 \u0441 \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u0442\u0430\u0439\u043b\u0430\u043c\u0438:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Application Cache manifest \u0434\u043b\u044f Mapbox (modestmaps)<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>CACHE MANIFEST  NETWORK: *  CACHE: index.html style.css event.js storage.js map.js run.js  mapbox.css mapbox.js map-controls.png <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <a href=\"http:\/\/tbicr.github.com\/OfflineMap\/mapbox\/index.html\">\u041f\u0440\u0438\u043c\u0435\u0440<\/a> \u0438 <a href=\"https:\/\/github.com\/tbicr\/OfflineMap\/tree\/master\/mapbox_idb_sql_site\">\u0435\u0433\u043e \u043a\u043e\u0434 \u043d\u0430 \u0433\u0438\u0442\u0445\u0430\u0431\u0435<\/a>.<\/p>\n<h4>OpenLayers<\/h4>\n<p>  \u0418 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u043c \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u043e\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0433\u043e JS API \u043a\u0430\u0440\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <a href=\"http:\/\/openlayers.org\/\">OpenLayers<\/a>.<\/p>\n<p>  \u041c\u043d\u0435 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u043e\u0442\u0440\u0430\u0442\u0438\u0442\u044c \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0432\u0438\u0434, \u0432 \u0438\u0442\u043e\u0433\u0435 \u043c\u043e\u0439 \u0444\u0430\u0439\u043b \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043f\u0440\u0438\u043e\u0431\u0440\u0435\u043b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0432\u0438\u0434:<\/p>\n<pre><code>[first]  [last]  [include] OpenLayers\/Map.js OpenLayers\/Layer\/OSM.js OpenLayers\/Control\/Zoom.js OpenLayers\/Control\/Navigation.js OpenLayers\/Control\/TouchNavigation.js  [exclude] <\/code><\/pre>\n<p>  \u042f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/github.com\/openlayers\/openlayers\/tree\/5b98769234dc20172a3cb6eeb77e60b297766e45\/lib\/OpenLayers\/Layer\/OSM.js#L50\"><code>OpenLayers.Layer.OSM<\/code><\/a>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0447\u043d\u0443 \u043f\u043e\u0438\u0441\u043a \u0441 \u043d\u0435\u0433\u043e:<\/p>\n<pre><code class=\"javascript\">url: [     'http:\/\/a.tile.openstreetmap.org\/${z}\/${x}\/${y}.png',     'http:\/\/b.tile.openstreetmap.org\/${z}\/${x}\/${y}.png',     'http:\/\/c.tile.openstreetmap.org\/${z}\/${x}\/${y}.png' ] <\/code><\/pre>\n<p>  <code>OpenLayers.Layer.OSM<\/code> \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 <code>OpenLayers.Layer.XYZ<\/code> \u0441 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c\u0438 URL. \u0417\u0434\u0435\u0441\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0435\u043d \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/github.com\/openlayers\/openlayers\/tree\/5b98769234dc20172a3cb6eeb77e60b297766e45\/lib\/OpenLayers\/Layer\/XYZ.js#L119\"><code>getURL<\/code><\/a>:<\/p>\n<pre><code class=\"javascript\">getURL: function (bounds) {     var xyz = this.getXYZ(bounds);     var url = this.url;     if (OpenLayers.Util.isArray(url)) {         var s = '' + xyz.x + xyz.y + xyz.z;         url = this.selectUrl(s, url);     }      return OpenLayers.String.format(url, xyz); } <\/code><\/pre>\n<p>  \u0422\u0430\u043a\u0436\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0435\u043d \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/github.com\/openlayers\/openlayers\/tree\/5b98769234dc20172a3cb6eeb77e60b297766e45\/lib\/OpenLayers\/Layer\/XYZ.js#L140\"><code>getXYZ<\/code><\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043b\u044e\u0447\u0430:<\/p>\n<pre><code class=\"javascript\">getXYZ: function(bounds) {     var res = this.getServerResolution();     var x = Math.round((bounds.left - this.maxExtent.left) \/         (res * this.tileSize.w));     var y = Math.round((this.maxExtent.top - bounds.top) \/         (res * this.tileSize.h));     var z = this.getServerZoom();      if (this.wrapDateLine) {         var limit = Math.pow(2, z);         x = ((x % limit) + limit) % limit;     }      return {'x': x, 'y': y, 'z': z}; } <\/code><\/pre>\n<p>  \u0421\u0430\u043c <code>OpenLayers.Layer.XYZ<\/code> \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 <code>OpenLayers.Layer.Grid<\/code>, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/github.com\/openlayers\/openlayers\/tree\/5b98769234dc20172a3cb6eeb77e60b297766e45\/lib\/OpenLayers\/Layer\/Grid.js#L1062\"><code>addTile<\/code><\/a> \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0435\u0431\u044f \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0442\u0430\u0439\u043b\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>tileClass<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <a href=\"https:\/\/github.com\/openlayers\/openlayers\/tree\/5b98769234dc20172a3cb6eeb77e60b297766e45\/lib\/OpenLayers\/Layer\/Grid.js#L59\"><code>OpenLayers.Tile.Image<\/code><\/a>:<\/p>\n<pre><code class=\"javascript\">addTile: function(bounds, position) {     var tile = new this.tileClass(         this, position, bounds, null, this.tileSize, this.tileOptions     );     this.events.triggerEvent(&quot;addtile&quot;, {tile: tile});     return tile; } <\/code><\/pre>\n<p>  \u0412 <code>OpenLayers.Tile.Image<\/code> <code>src<\/code> \u0437\u0430\u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <a href=\"https:\/\/github.com\/openlayers\/openlayers\/tree\/5b98769234dc20172a3cb6eeb77e60b297766e45\/lib\/OpenLayers\/Tile\/Image.js#L347\"><code>setImgSrc<\/code><\/a>:<\/p>\n<pre><code class=\"javascript\">setImgSrc: function(url) {     var img = this.imgDiv;     if (url) {         img.style.visibility = 'hidden';         img.style.opacity = 0;         if (this.crossOriginKeyword) {             if (url.substr(0, 5) !== 'data:') {                 img.setAttribute(&quot;crossorigin&quot;, this.crossOriginKeyword);             } else {                 img.removeAttribute(&quot;crossorigin&quot;);             }         }         img.src = url;     } else {         this.stopLoading();         this.imgDiv = null;         if (img.parentNode) {             img.parentNode.removeChild(img);         }     } } <\/code><\/pre>\n<p>  \u041d\u043e \u0432 \u043d\u0435\u043c \u043d\u0435 \u0437\u0430\u0434\u0430\u044e\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 <code>onload<\/code> \u0438 <code>onerror<\/code>. \u0421\u0430\u043c \u043c\u0435\u0442\u043e\u0434 \u0434\u0435\u0440\u0433\u0430\u0435\u0442\u0441\u044f \u0438\u0437 <a href=\"https:\/\/github.com\/openlayers\/openlayers\/tree\/5b98769234dc20172a3cb6eeb77e60b297766e45\/lib\/OpenLayers\/Tile\/Image.js#L315\"><code>initImage<\/code><\/a>, \u0433\u0434\u0435 \u044d\u0442\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0438 \u0432\u0435\u0448\u0430\u044e\u0442\u0441\u044f:<\/p>\n<pre><code class=\"javascript\">initImage: function() {     this.events.triggerEvent('beforeload');     this.layer.div.appendChild(this.getTile());     this.events.triggerEvent(this._loadEvent);     var img = this.getImage();     if (this.url && img.getAttribute(&quot;src&quot;) == this.url) {         this._loadTimeout = window.setTimeout(             OpenLayers.Function.bind(this.onImageLoad, this), 0         );     } else {         this.stopLoading();         if (this.crossOriginKeyword) {             img.removeAttribute(&quot;crossorigin&quot;);         }         OpenLayers.Event.observe(img, &quot;load&quot;,             OpenLayers.Function.bind(this.onImageLoad, this)         );         OpenLayers.Event.observe(img, &quot;error&quot;,             OpenLayers.Function.bind(this.onImageError, this)         );         this.imageReloadAttempts = 0;         this.setImgSrc(this.url);     } } <\/code><\/pre>\n<p>  \u041c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043c\u0435\u0442\u043e\u0434 \u043a\u043b\u0430\u0441\u0441\u0430 \u0441\u043b\u043e\u044f <code>getURL<\/code>, \u0430 \u0442\u0430\u043a\u0436\u0435 <code>initImage<\/code>, \u0434\u0435\u0440\u0433\u0430\u044e\u0442\u0441\u044f \u0438\u0437 <a href=\"https:\/\/github.com\/openlayers\/openlayers\/tree\/5b98769234dc20172a3cb6eeb77e60b297766e45\/lib\/OpenLayers\/Tile\/Image.js#L197\"><code>renderTile<\/code><\/a>:<\/p>\n<pre><code class=\"javascript\">renderTile: function() {     if (this.layer.async) {         var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;         this.layer.getURLasync(this.bounds, function(url) {             if (id == this.asyncRequestId) {                 this.url = url;                 this.initImage();             }         }, this);     } else {         this.url = this.layer.getURL(this.bounds);         this.initImage();     } } <\/code><\/pre>\n<p>  \u0418\u0442\u0430\u043a \u0435\u0441\u043b\u0438 \u043c\u044b \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441, \u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u043b\u044f OpenLayers<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">var StorageImageTile = OpenLayers.Class(OpenLayers.Tile.Image, {     _imageToDataUri: function (image) {         var canvas = window.document.createElement('canvas');         canvas.width = image.width;         canvas.height = image.height;          var context = canvas.getContext('2d');         context.drawImage(image, 0, 0);          return canvas.toDataURL('image\/png');     },      onImageLoadWithCache: function() {         if (this.storage) {             this.storage.add(this._storageKey, this._imageToDataUri(this.imgDiv));         }         this.onImageLoad.apply(this, arguments);     },      renderTile: function() {         var self = this;         var xyz = this.layer.getXYZ(this.bounds);         var key = xyz.z + ',' + xyz.y + ',' + xyz.x;         var url = this.layer.getURL(this.bounds);         if (this.storage) {             this.storage.get(key, function (value) {                 if (value) {                     self.initImage(key, value, false);                 } else {                     self.initImage(key, url, true);                 }             }, function () {                 self.initImage(key, url, true);             });         } else {             self.initImage(key, url, false);         }     },      initImage: function(key, url, cache) {         this.events.triggerEvent('beforeload');         this.layer.div.appendChild(this.getTile());         this.events.triggerEvent(this._loadEvent);         var img = this.getImage();          this.stopLoading();         if (cache) {             OpenLayers.Event.observe(img, 'load',                 OpenLayers.Function.bind(this.onImageLoadWithCache, this)             );             this._storageKey = key;         } else {             OpenLayers.Event.observe(img, 'load',                 OpenLayers.Function.bind(this.onImageLoad, this)             );         }         OpenLayers.Event.observe(img, 'error',             OpenLayers.Function.bind(this.onImageError, this)         );         this.imageReloadAttempts = 0;         this.setImgSrc(url);     } });  var StorageOSMLayer = OpenLayers.Class(OpenLayers.Layer.OSM, {     async: true,     tileClass: StorageImageTile,      initialize: function(name, url, options) {         OpenLayers.Layer.OSM.prototype.initialize.apply(this, arguments);         this.tileOptions = OpenLayers.Util.extend({             storage: options.storage         }, this.options && this.options.tileOptions);     },      clone: function (obj) {         if (obj == null) {             obj = new StorageOSMLayer(this.name,                 this.url,                 this.getOptions());         }          obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);         return obj;     } }); <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0421\u0430\u043c\u0430 \u0436\u0435 \u043a\u0430\u0440\u0442\u0430 \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"javascript\">var map = new OpenLayers.Map('map'); map.addLayer(new StorageOSMLayer(undefined, undefined, {storage: storage})); var fromProjection = new OpenLayers.Projection('EPSG:4326'); var toProjection   = new OpenLayers.Projection('EPSG:900913'); var center = new OpenLayers.LonLat(27.561850, 53.902254).transform(fromProjection, toProjection); map.setCenter(center, 13); <\/code><\/pre>\n<p>  \u0422\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430\u0448\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0432 Application Cache, \u0447\u0442\u043e\u0431\u044b \u043a\u0430\u0440\u0442\u0430 \u043c\u043e\u0433\u043b\u0430 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0431\u0435\u0437 \u0441\u0435\u0442\u0438 \u0441 \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u0442\u0430\u0439\u043b\u0430\u043c\u0438:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Application Cache manifest \u0434\u043b\u044f OpenLayers<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>CACHE MANIFEST  NETWORK: *  CACHE: index.html style.css event.js storage.js map.js run.js  theme\/default\/style.css OpenLayers.js <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <a href=\"http:\/\/tbicr.github.com\/OfflineMap\/openlayers\/index.html\">\u041f\u0440\u0438\u043c\u0435\u0440<\/a> \u0438 <a href=\"https:\/\/github.com\/tbicr\/OfflineMap\/tree\/master\/openlayers_idb_sql_site\">\u0435\u0433\u043e \u043a\u043e\u0434 \u043d\u0430 \u0433\u0438\u0442\u0445\u0430\u0431\u0435<\/a>.<\/p>\n<p>  \u0415\u0449\u0435 \u044f \u043d\u0430\u0448\u0435\u043b \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043a\u044d\u0448\u0430 <a href=\"https:\/\/github.com\/openlayers\/openlayers\/tree\/5b98769234dc20172a3cb6eeb77e60b297766e45\/lib\/OpenLayers\/Control\/CacheWrite.js\"><code>OpenLayers.Control.CacheWrite<\/code><\/a>, \u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <code>localStorage<\/code>, \u0447\u0442\u043e \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e.<\/p>\n<h4>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h4>\n<p>  \u0421\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0447\u0442\u043e \u0445\u043e\u0442\u0435\u043b. \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0448\u0443\u0441\u0442\u0440\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0432 \u0445\u0440\u043e\u043c\u0435 \u0438\u043b\u0438 \u0435\u0441\u043b\u0438 \u0443 \u0412\u0430\u0441 SSD, \u0432 \u043b\u0438\u0441\u0435 \u0438 \u043e\u043f\u0435\u0440\u0435 \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u0440\u043c\u0430\u0437\u0430 \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 \u043d\u0430 \u0434\u0438\u0441\u043a, \u043e\u0441\u043b\u0430 \u043f\u043e\u0434 \u0440\u0443\u043a\u043e\u0439 \u043d\u0435 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c. \u0422\u043e\u0440\u043c\u043e\u0437\u0430 \u043f\u0440\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0438 \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u0445, \u043f\u0440\u0438\u0447\u0435\u043c \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 \u0447\u0442\u0435\u043d\u0438\u0438, \u0447\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043c\u0435\u043d\u044f \u0440\u0430\u0441\u0441\u0442\u0440\u043e\u0438\u043b\u043e, \u0442\u043a \u0434\u043b\u044f \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0434\u0430\u043d\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0431\u043e\u043b\u0435\u0435 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u0430.<\/p>\n<p>  \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 <code>IndexedDB<\/code> \u0438\u043b\u0438 <code>WebSQL<\/code> \u0432\u043f\u043e\u043b\u043d\u0435 \u0445\u0432\u0430\u0442\u0438\u0442 \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0433\u043e\u0440\u043e\u0434 \u0438\u043b\u0438 \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0431\u043e\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u043c \u0447\u0435\u043c \u0432 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0435 \u0441 <code>localstrage<\/code>.<\/p>\n<p>  \u0412 \u043c\u043e\u0438\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u0445 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430\u043c\u0438, \u043d\u043e \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0435\u0433\u043e \u043a\u043e\u043c\u0444\u043e\u0440\u0442\u0430 \u043d\u0443\u0436\u043d\u043e \u0435\u0449\u0435 \u043f\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u0434 \u0438\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0432\u044b\u0441\u0438\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 \u0442\u0435\u043c \u0447\u0442\u043e \u0435\u0441\u0442\u044c \u0441\u0435\u0439\u0447\u0430\u0441.    \t \t\t \t\t\t \t<\/p>\n<div class=\"polling\">\n<form action=\"\/json\/polling\/\" class=\"poll\" method=\"post\">\n<div class=\"poll_title\">\u041a\u0430\u043a\u0438\u043c\u0438 JS API \u043a\u0430\u0440\u0442 \u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435\u0441\u044c \u0412\u044b?<\/div>\n<p>  \t\t<input type=\"hidden\" name=\"post_id\" value=\"170129\"\/> \t\t<input type=\"hidden\" name=\"polling_question_id\" value=\"7421\"\/>  \t\t<\/p>\n<table class=\"answer\">\n<tr>\n<td class=\"input\"> \t\t\t\t\t<input type=\"checkbox\" id=\"vv38583\" \t\t\t\t\t\tclass=\"checkbox js-field-data\" \t\t\t\t\t\tname=\"variant[]\" \t\t\t\t\t\tvalue=\"38583\" \/> \t\t\t\t<\/td>\n<td class=\"label\"> \t\t\t\t\t<label for=\"vv38583\">Google Maps (https:\/\/developers.google.com\/maps\/documentation\/javascript\/)<\/label> \t\t\t\t<\/td>\n<\/tr>\n<tr>\n<td class=\"input\"> \t\t\t\t\t<input type=\"checkbox\" id=\"vv38585\" \t\t\t\t\t\tclass=\"checkbox js-field-data\" \t\t\t\t\t\tname=\"variant[]\" \t\t\t\t\t\tvalue=\"38585\" \/> \t\t\t\t<\/td>\n<td class=\"label\"> \t\t\t\t\t<label for=\"vv38585\">Bing Maps (http:\/\/www.microsoft.com\/maps\/developers\/web.aspx)<\/label> \t\t\t\t<\/td>\n<\/tr>\n<tr>\n<td class=\"input\"> \t\t\t\t\t<input type=\"checkbox\" id=\"vv38587\" \t\t\t\t\t\tclass=\"checkbox js-field-data\" \t\t\t\t\t\tname=\"variant[]\" \t\t\t\t\t\tvalue=\"38587\" \/> \t\t\t\t<\/td>\n<td class=\"label\"> \t\t\t\t\t<label for=\"vv38587\">Nokia (Here) Maps (http:\/\/developer.here.com\/)<\/label> \t\t\t\t<\/td>\n<\/tr>\n<tr>\n<td class=\"input\"> \t\t\t\t\t<input type=\"checkbox\" id=\"vv38589\" \t\t\t\t\t\tclass=\"checkbox js-field-data\" \t\t\t\t\t\tname=\"variant[]\" \t\t\t\t\t\tvalue=\"38589\" \/> \t\t\t\t<\/td>\n<td class=\"label\"> \t\t\t\t\t<label for=\"vv38589\">Yandex Maps (http:\/\/api.yandex.ru\/maps\/)<\/label> \t\t\t\t<\/td>\n<\/tr>\n<tr>\n<td class=\"input\"> \t\t\t\t\t<input type=\"checkbox\" id=\"vv38591\" \t\t\t\t\t\tclass=\"checkbox js-field-data\" \t\t\t\t\t\tname=\"variant[]\" \t\t\t\t\t\tvalue=\"38591\" \/> \t\t\t\t<\/td>\n<td class=\"label\"> \t\t\t\t\t<label for=\"vv38591\">2gis (http:\/\/api.2gis.ru\/)<\/label> \t\t\t\t<\/td>\n<\/tr>\n<tr>\n<td class=\"input\"> \t\t\t\t\t<input type=\"checkbox\" id=\"vv38593\" \t\t\t\t\t\tclass=\"checkbox js-field-data\" \t\t\t\t\t\tname=\"variant[]\" \t\t\t\t\t\tvalue=\"38593\" \/> \t\t\t\t<\/td>\n<td class=\"label\"> \t\t\t\t\t<label for=\"vv38593\">Leaflet (http:\/\/leafletjs.com)<\/label> \t\t\t\t<\/td>\n<\/tr>\n<tr>\n<td class=\"input\"> \t\t\t\t\t<input type=\"checkbox\" id=\"vv38595\" \t\t\t\t\t\tclass=\"checkbox js-field-data\" \t\t\t\t\t\tname=\"variant[]\" \t\t\t\t\t\tvalue=\"38595\" \/> \t\t\t\t<\/td>\n<td class=\"label\"> \t\t\t\t\t<label for=\"vv38595\">Mapbox (http:\/\/mapbox.com\/mapbox.js)<\/label> \t\t\t\t<\/td>\n<\/tr>\n<tr>\n<td class=\"input\"> \t\t\t\t\t<input type=\"checkbox\" id=\"vv38597\" \t\t\t\t\t\tclass=\"checkbox js-field-data\" \t\t\t\t\t\tname=\"variant[]\" \t\t\t\t\t\tvalue=\"38597\" \/> \t\t\t\t<\/td>\n<td class=\"label\"> \t\t\t\t\t<label for=\"vv38597\">OpenLayers (http:\/\/openlayers.org)<\/label> \t\t\t\t<\/td>\n<\/tr>\n<tr>\n<td class=\"input\"> \t\t\t\t\t<input type=\"checkbox\" id=\"vv38599\" \t\t\t\t\t\tclass=\"checkbox js-field-data\" \t\t\t\t\t\tname=\"variant[]\" \t\t\t\t\t\tvalue=\"38599\" \/> \t\t\t\t<\/td>\n<td class=\"label\"> \t\t\t\t\t<label for=\"vv38599\">\u0414\u0440\u0443\u0433\u043e\u0435 (\u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438)<\/label> \t\t\t\t<\/td>\n<\/tr>\n<\/table>\n<p class=\"for_users_only_msg\">\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u043f\u0440\u043e\u0441\u0435. <a href=\"\/login\/\">\u0412\u043e\u0439\u0434\u0438\u0442\u0435<\/a>, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430.<\/p>\n<p class=\"total\">\u041f\u0440\u043e\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043b 1 \u0447\u0435\u043b\u043e\u0432\u0435\u043a. \u0412\u043e\u0437\u0434\u0435\u0440\u0436\u0430\u0432\u0448\u0438\u0445\u0441\u044f \u043d\u0435\u0442.<\/p>\n<\/p><\/form>\n<\/p><\/div>\n<div class=\"clear\"><\/div>\n<\/p><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"http:\/\/habrahabr.ru\/post\/170129\/\"> http:\/\/habrahabr.ru\/post\/170129\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"content html_format\">   \t\u041a\u0430\u043a-\u0442\u043e \u0434\u0430\u0432\u043d\u043e \u044f <a href=\"http:\/\/habrahabr.ru\/post\/141707\/\">\u043f\u0438\u0441\u0430\u043b \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0432 \u0432\u0435\u0431\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0440\u0442\u044b \u0431\u0435\u0437 \u0441\u0435\u0442\u0438<\/a> \u0438 \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0433\u0443\u0433\u043b\u043e \u043a\u0430\u0440\u0442. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u043b\u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441\u044b, \u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u043c\u043d\u043e\u044e \u043a\u043e\u0434 \u0440\u0430\u0431\u043e\u0442\u0430\u043b \u0442\u043e\u043b\u044c\u043a\u043e \u0441 <code>localstorage<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u0440\u0435\u0448\u0438\u043b \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043d\u0430 \u0441\u0432\u0435\u0442\u043b\u0443\u044e \u0441\u0442\u043e\u0440\u043e\u043d\u0443 \u0441\u0438\u043b\u044b, \u0433\u0434\u0435 \u043a\u043e\u0434 \u043e\u0442\u043a\u0440\u044b\u0442, \u043f\u0440\u043e\u0441\u0442 \u0438 \u043f\u043e\u043d\u044f\u0442\u0435\u043d.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-170129","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/170129","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=170129"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/170129\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=170129"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=170129"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=170129"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}