IndexedDB - рдЕрд╕реАрдорд┐рдд рдбреЗрдЯрд╛ рднрдВрдбрд╛рд░рдг

рд╢реБрдн рджреЛрдкрд╣рд░, рдкреНрд░рд┐рдп рд╕рдореБрджрд╛рдпред
рдЙрди рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдЬреЛ рдЗрдВрдбреЗрдХреНрд╕рдбреАрдбреАрдмреА рдХреНрдпрд╛ рд╣реИ рдФрд░ рдЗрд╕рдХреЗ рд╕рд╛рде рдХреНрдпрд╛ рдЦрд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЖрдкрдХреЛ рдЬрд╛рдирдХрд╛рд░реА рдирд╣реАрдВ рд╣реИ, рдЖрдк рдпрд╣рд╛рдВ рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВред

рдФрд░ рд╣рдо рдЖрдЧреЗ рдмрдврд╝рддреЗ рд╣реИрдВред

рдЕрд╕реАрдорд┐рдд


рдЬрд┐рд╕ рдХрд╛рд░реНрдпрд╛рд▓рдп рдореЗрдВ рдореИрдВ рдХрд╛рдо рдХрд░рддрд╛ рд╣реВрдВ, рд╡рд╣рд╛рдВ рдХреНрд▓рд╛рдЗрдВрдЯ рдкрдХреНрд╖ рдкрд░ рдПрдХ рдЕрдиреБрдХреНрд░рдорд┐рдд рд╕реНрдерд╛рдиреАрдп рдбреЗрдЯрд╛рдмреЗрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдереА рдФрд░ рд╡рд┐рдХрд▓реНрдк рддреБрд░рдВрдд IndexedDB рдкрд░ рдЧрд┐рд░ рдЧрдпрд╛ред

рд▓реЗрдХрд┐рди рд╣рдореЗрд╢рд╛ рдХреА рддрд░рд╣ рдПрдХ "рдмреНрдпреВрдЯ" рд╣реИ, рдпрд╣ рдмрд╣реБрдд "рдмреНрдпреВрдЯ" рд╣реИ - рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдорд╢реАрди рдкрд░ рдбреЗрдЯрд╛рдмреЗрд╕ рдХрд╛ рдЖрдХрд╛рд░ рд╕реАрдорд╛ 5 рдПрдордмреА рд╣реИ, рдЬреЛ рд╣рдореЗрдВ рдмрд┐рд▓реНрдХреБрд▓ рднреА рдкрд╕рдВрдж рдирд╣реАрдВ рдЖрдпрд╛ред рдЪреВрдВрдХрд┐ рдЗрд╕ рддрдХрдиреАрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рд╣рдорд╛рд░реА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреЗ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдкреИрдирд▓ рдореЗрдВ рдХрд░рдиреЗ рдХреА рдпреЛрдЬрдирд╛ рдереА, рдФрд░ рд╕рднреА рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ Google рдХреНрд░реЛрдо рдХрд╛ рдЙрдкрдпреЛрдЧ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдХреЗ рд░реВрдк рдореЗрдВ рдХрд░рддреЗ рдереЗ, рдкреНрд░реЙрдХреНрд╕реА рдПрдХреНрд╕рдЯреЗрдВрд╢рди рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЙрд╕ рдкреНрд░рддрд┐рдмрдВрдз рдХреЗ рд▓рд┐рдП рд╡рд░реНрдХрдЕрд░рд╛рдЙрдВрдб рдЦреЛрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдирд┐рд░реНрдгрдп рд▓рд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рдмрд╣реБрдд рд╕рд╛рд░реА рдЬрд╛рдирдХрд╛рд░рд┐рдпреЛрдВ рдХреЛ рдЬрд╛рдирдиреЗ рдХреЗ рдмрд╛рдж, рд╣рдо рдЗрд╕ рдирддреАрдЬреЗ рдкрд░ рдкрд╣реБрдБрдЪреЗ рдХрд┐ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рдЖрдХрд╛рд░ рдкрд░ рдкреНрд░рддрд┐рдмрдВрдз рд╣рдорд╛рд░реЗ рд╡рд┐рд╕реНрддрд╛рд░ рдХреА рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рдореЗрдВ рд╡рд┐рд╢реЗрд╖ рдЭрдВрдбреЗ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╣рдЯрд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:

"permissions": [ "unlimitedStorage", "unlimited_storage" ], 



рдкреЛрд╕реНрдЯрд┐рдВрдЧ рд╕рд╛рдЗрдЯ-рдПрдХреНрд╕рдЯреЗрдВрд╢рди-рд╕рд╛рдЗрдЯ


рд╣рдо рдФрд░ рдЖрдЧреЗ рдмрдврд╝реЗрдВред рд╣рдордиреЗ рдЕрд╕реАрдорд┐рдд рдбреЗрдЯрд╛ рд╕рдВрдЧреНрд░рд╣рдг рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдпрд╛, рд▓реЗрдХрд┐рди рдЕрдм рд╕рд╛рдЗрдЯ рд╕реЗ рд╕реАрдзреЗ рдЙрд╕реА рдЕрд╕реАрдорд┐рдд рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЗрд╕рдХреЗ рд▓рд┐рдП, рд╕рд╛рдЗрдЯ рдФрд░ рдПрдХреНрд╕рдЯреЗрдВрд╢рди рдХреЗ рдмреАрдЪ рд╕рдВрджреЗрд╢ рднреЗрдЬрдиреЗ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ (рдПрдХреНрд╕рдЯреЗрдВрд╢рди рдиреЗ рд╕рд╛рдЗрдЯ рдФрд░ рдЕрд╕реАрдорд┐рдд рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рдмреАрдЪ рдкреНрд░реЙрдХреНрд╕реА рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рдо рдХрд┐рдпрд╛)ред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдорд╛рд░реЗ рд╡рд┐рд╕реНрддрд╛рд░ рдХреЗ рдкреНрд░рдХрдЯрди рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЭрдВрдбреЗ рдЬреЛрдбрд╝реЗ рдЧрдП:

 "externally_connectable": { "matches": [ "*://localhost/*", "____URL " ] } 


рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рдХрд┐ рдлреЙрд░реНрдо рдХреЗ URL: *: //google.com/* рдФрд░ http: //*.chromium.org/*, рд▓реЗрдХрд┐рди, http: // * / *, *: / / * рдХреЛ рд╡реИрдз рдорд╛рдирд╛ рдЬрд╛рддрд╛ рд╣реИред рдХреЙрдо / рдирд╣реАрдВ рд╣реИрдВред
рдЖрдк рдпрд╣рд╛рдБ рдмрд╛рд╣рд░реА рд░реВрдк рд╕реЗ рдЕрдзрд┐рдХ рдЬрд╛рдирдХрд╛рд░реА рдХреЗ рд▓рд┐рдП рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВред

рд╣рдо рдФрд░ рдЖрдЧреЗ рдмрдврд╝реЗрдВред

рд╕рд╛рдЗрдЯ рдФрд░ рдбреЗрдЯрд╛рдмреЗрд╕ рддрдХ рдкрд╣реБрдВрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд┐рд╕реНрддрд╛рд░ рдХреЗ рдмреАрдЪ рдмрд╣реБрдд "рдкреБрд▓" рд▓рд┐рдЦрдиреЗ рдХрд╛ рдЪрд░рдг рдЖ рдЧрдпрд╛ рд╣реИред
рдПрдХреНрд╕рдЯреЗрдВрд╢рди рд╕рд╛рдЗрдб рдкрд░ IndexedDB рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП Db.js рдХреЛ рдореБрдЦреНрдп рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреЗ рд░реВрдк рдореЗрдВ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдЬрд┐рд╕реЗ рдЖрдк рдпрд╣рд╛рдВ рдкрд╛ рд╕рдХрддреЗ рд╣реИрдВред

рдкрд╣рд┐рдпрд╛ рдХреЛ рд╕реБрджреГрдврд╝ рдирд╣реАрдВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╕рд╛рдЗрдЯ рд╕рд╛рдЗрдб рдкрд░ рдПрдХреНрд╕реЗрд╕ рд╕рд┐рдВрдЯреИрдХреНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдирд┐рд░реНрдгрдп рд▓рд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдЬрд┐рд╕реЗ db.js.

рд╡рд┐рд╕реНрддрд╛рд░


рдФрд░ рдЗрд╕рд▓рд┐рдП рдЪрд▓рд┐рдП, background.js рдмрдирд╛рддреЗ рд╣реИрдВ, рдЬрд┐рд╕реЗ рд╣рдо рдЖрдиреЗ рд╡рд╛рд▓реЗ рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рд╕реБрдиреЗрдВрдЧреЗ, рдФрд░ рдЙрдирдХрд╛ рдЬрд╡рд╛рдм рджреЗрдВрдЧреЗред рдиреАрдЪреЗ рджрд┐рдП рдЧрдП рдХреЛрдб рдХреЛ рд╕реВрдЪреАрдмрджреНрдз рдХрд░рдирд╛:

 var server; chrome.runtime.onMessageExternal.addListener( function (request, sender, sendResponse) { var cmd = request.cmd, params = request.params; try { switch (cmd) { case "getUsageAndQuota": navigator.webkitPersistentStorage.queryUsageAndQuota(function(u,q){ sendResponse({"usage": u,"quota":q}); }); break; case "open": db.open(params).done(function (s) { server = s; var exclude = "add close get query remove update".split(" "); var tables = new Array(); for(var table in server){ if(exclude.indexOf(table)==-1){ tables.push(table); } } sendResponse(tables); }); break; case "close": server.close(); sendResponse({}); break; case "get": server[request.table].get(params).done(sendResponse) break; case "add": server[request.table].add(params).done(sendResponse); break; case "update": server[request.table].update(params).done(sendResponse); break; case "remove": server[request.table].remove(params).done(sendResponse); break; case "execute": var tmp_server = server[request.table]; var query = tmp_server.query.apply(tmp_server, obj2arr(request.query)); var flt; for (var i = 0; i < request.filters.length; i++) { flt = request.filters[i]; if (flt.type == "filter") { flt.args = new Function("item", flt.args[0]); } query = query[flt.type].apply(query, obj2arr(flt.args)); } query.execute().done(sendResponse); break; } } catch (error) { if (error.name != "TypeError") { sendResponse({RUNTIME_ERROR: error}); } } return true; }); 


рд▓реЗрдХрд┐рди рдпрд╣рд╛рдБ рдПрдХ рдЖрд╢реНрдЪрд░реНрдп рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХреА рдЧрдИ, рдЕрд░реНрдерд╛рддреН рдХреЛрдб рдХреЗ рдПрдХ рдЦрдВрдб рдХреЗ рдирд┐рд╖реНрдкрд╛рджрди рдкрд░:

 flt.args = new Function("item", flt.args[0]); 


рд╣рдореЗрдВ рдПрдХ рдЕрдкрд╡рд╛рдж рдорд┐рд▓рддрд╛ рд╣реИ:

Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:". ред

рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдкреНрд░рдХрдЯ рд╣реЛрдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдФрд░ рдкрдВрдХреНрддрд┐ рдЬреЛрдбрд╝реЗрдВ рдЬреЛ рдПрдХреНрд╕рдЯреЗрдВрд╢рди рд╕рд╛рдЗрдб рдкрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдЬреЗрдПрд╕ рдХреЗ рдирд┐рд╖реНрдкрд╛рджрди рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред

 "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" 


рдореБрдЭреЗ рдХрд┐рд╕реА рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ рд╕рд░рдгреА рдореЗрдВ рд▓реЗ рдЬрд╛рдиреЗ рдХрд╛ рдПрдХ рд╕рд╣рд╛рдпрдХ рдлрд╝рдВрдХреНрд╢рди рд▓рд╛рдЧреВ рдХрд░рдирд╛ рдерд╛, рдЗрд╕реЗ рдлрд╝рдВрдХреНрд╢рди рдХреЗ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╛рд╕ рдХрд░рдирд╛ рдерд╛ред

 var obj2arr = function (obj) { if (typeof obj == 'object') { var tmp_args = new Array(); for (var k in obj) { tmp_args.push(obj[k]); } return tmp_args; } else { return [obj]; } } 


рдкреВрд░реНрдг рд▓рд┐рд╕реНрдЯрд┐рдВрдЧ рдореЗрдирд┐рдлрд╝реЗрд╕реНрдЯ

 { "manifest_version": 2, "name": "exDB", "description": "This extension give proxy access to indexdb from page.", "version": "1.0", "background": { "page": "background.html" }, "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", "externally_connectable": { "matches": [ "*://localhost/*" ] }, "permissions": [ "unlimitedStorage", "unlimited_storage" ], "icons": { "16": "icons/icon_016.png", "48": "icons/icon_048.png" } } 


рдЧреНрд░рд╛рд╣рдХ


рд╣рдордиреЗ рдПрдХреНрд╕рдЯреЗрдВрд╢рди рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛ рд▓рд┐рдпрд╛ рд╣реИ, рдЕрдм рдЕрдкрдиреЗ рдкреНрд░реЙрдХреНрд╕реА рдПрдХреНрд╕рдЯреЗрдВрд╢рди рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреНрд▓рд╛рдЗрдВрдЯ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рд▓рд┐рдЦрдирд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред
рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЗ рдПрдХ рд╕рдВрджреЗрд╢ рднреЗрдЬрддреЗ рд╕рдордп, рдкрд╣рд▓реА рдЪреАрдЬ рдЬреЛ рдЖрд╡рд╢реНрдпрдХ рд╣реИ, рдпрд╣ рдЗрдВрдЧрд┐рдд рдХрд░реЗрдВ рдХрд┐ рд╣рдо рдЗрд╕реЗ рдХрд┐рд╕ рдПрдХреНрд╕рдЯреЗрдВрд╢рди рдкрд░ рднреЗрдЬрдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдЗрд╕рдХреЗ рд▓рд┐рдП, рдЗрд╕рдХрд╛ рд╕рдВрдХреЗрдд рджреЗрдВ:

 chrome.runtime.sendMessage("ID_", data, callback); 


рдкреВрд░реНрдг рдЧреНрд░рд╛рд╣рдХ рдкреБрд╕реНрддрдХрд╛рд▓рдп рд╕реВрдЪреА:

 (function (window, undefined) { "use strict"; function exDB() { var self = this; this.extensionId = arguments[0] || "eojllnbjkomphhmpcpafaipblnembfem"; this.filterList = new Array(); this._table; this._query; self.sendMessage = function sendMessage(data, callback) { chrome.runtime.sendMessage(self.extensionId, data, callback); }; self.open = function (params, callback) { self.sendMessage({"cmd": "open", "params": params}, function(r){ var tn; for(var i=0;i< r.length;i++) tn = r[i]; self.__defineGetter__(tn,function(){ self._table = tn; return this; }); callback(); }); return self; }; self.close = function (callback) { self.sendMessage({"cmd": "close", "params": {}}, callback); return self; } self.table = function (name) { self._table = name; return self; }; self.query = function () { self._query = arguments; return self; }; self.execute = function (callback) { self.sendMessage({"cmd": "execute", "table": self._table, "query": self._query, "filters": self.filterList}, function (result) { if (result && result.RUNTIME_ERROR) { console.error(result.RUNTIME_ERROR.message); result = null; } callback(result); }); self._query = null; self.filterList = []; }; self.getUsageAndQuota = function(callback){ self.sendMessage({"cmd": "getUsageAndQuota"},callback); }; "add update remove get".split(" ").forEach(function (fn) { self[fn] = function (item, callback) { self.sendMessage({"cmd": fn, "table": self._table, "params": item}, function (result) { if (result && result.RUNTIME_ERROR) { console.error(result.RUNTIME_ERROR.message); result = null; } callback(result); }); return self; } }); "all only lowerBound upperBound bound filter desc distinct keys count".split(" ").forEach(function (fn) { self[fn] = function () { self.filterList.push({type: fn, args: arguments}); return self; } }); } window.exDB = exDB; })(window, undefined); 


рдЗрд╕ рд╕реНрддрд░ рдкрд░, рдЕрд╕реАрдорд┐рдд indexDB рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░рд╛ рдкрд░рд┐рд╕рд░ рддреИрдпрд╛рд░ рд╣реИред рдиреАрдЪреЗ рдЙрдкрдпреЛрдЧ рдХреЗ рдЙрджрд╛рд╣рд░рдг рд╣реИрдВред

рд╕рдВрдмрдВрдз

  var db = new exDB(); db.open({ server: 'my-app', version: 1, schema: { people: { key: { keyPath: 'id', autoIncrement: true }, // Optionally add indexes indexes: { firstName: { }, answer: { unique: true } } } } }, function () {}); 

рдХрдиреЗрдХреНрд╢рди рдмрдВрдж рдХрд░реЗрдВ

 db.close(); 

рд░рд┐рдХреЙрд░реНрдб рдЬреЛрдбрд╝реЗрдВ

 db.table("people").add({ firstName: 'Aaron', lastName: 'Powell', answer: 142},function(r){ }); 


рд░рд┐рдХреЙрд░реНрдб рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВ

  db.table("people").update({ id:1, firstName: 'Aaron', lastName: 'Powell', answer: 1242}, function (r) {}); 


рдЖрдИрдбреА рджреНрд╡рд╛рд░рд╛ рд░рд┐рдХреЙрд░реНрдб рд╣рдЯрд╛рдПрдВ

 db.table("people").remove(1,function(key){}); 


рдЖрдИрдбреА рджреНрд╡рд╛рд░рд╛ рд░рд┐рдХреЙрд░реНрдб рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛

 db.table("people").get(1,function(r){ console.log(r); }); 


рд▓реЗ рдЖрдУ / рдЫрд╛рдБрдЯреЛ

 db.people.query("firstName").only("Aaron2").execute(function(r){ console.log("GETTER",r); }); db.table("people").query("answer").all().desc().execute(function(r){ console.log("all",r); }); db.table("people").query("answer").only(12642).count().execute(function(r){ console.log("only",r); }); db.table("people").query("answer").bound(20,45).execute(function(r){ console.log("bound",r); }); db.table("people").query("answer").lowerBound(50).keys().execute(function(r){ console.log("lowerBound",r); }); db.table("people").query("answer").upperBound(43).execute(function(r){ console.log("upperBound",r); }); db.table("people").query("answer").filter("return item.answer==42 && item.firstName=='Aaron'").execute(function(r){ console.log("filter",r); }); 

рдбреЗрдЯрд╛рдмреЗрд╕ рдХрд╛ рдЖрдХрд╛рд░ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ (рдкреНрд░рдпреБрдХреНрдд / рдЕрдзрд┐рдХрддрдо рдЖрдХрд╛рд░)

 db.getUsageAndQuota(function(r){ console.log("used", r.usage); //bytes console.log("quota", r.quota); //bytes }); 


рдирд┐рд╖реНрдХрд░реНрд╖


рдЖрдЬ рдпрд╣ рд╕рдорд╛рдзрд╛рди рд╕рдХреНрд░рд┐рдп рд░реВрдк рд╕реЗ рд╣рдорд╛рд░реА рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдкрд░ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдореИрдВ рд░рдЪрдирд╛рддреНрдордХ рдЖрд▓реЛрдЪрдирд╛ рдФрд░ рд╕реБрдЭрд╛рд╡реЛрдВ рдХреЗ рд▓рд┐рдП рдЖрднрд╛рд░реА рд░рд╣реВрдВрдЧрд╛ред рдЪреВрдВрдХрд┐ рдпрд╣ рд╣рдм рдкрд░ рдореЗрд░рд╛ рдкрд╣рд▓рд╛ рд▓реЗрдЦ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдореИрдВ рдЖрдкрд╕реЗ рдХрд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдЖрдк рдЕрдзрд┐рдХ рдиреНрдпрд╛рдп рди рдХрд░реЗрдВред

рдЖрдк рдЬреАрдердм рдкрд░ рд╕реНрд░реЛрдд рдХреЛрдб рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

Source: https://habr.com/ru/post/In198666/


All Articles