Repository: A/largescaleJS_ru Branch: gh-pages Commit: 3708e4b2fb5c Files: 114 Total size: 12.7 MB Directory structure: gitextract_w4job09g/ ├── .gitignore ├── Gemfile ├── LICENSE.md ├── Makefile ├── README.md ├── _config.yml ├── _includes/ │ ├── adsense.html │ ├── analytics.html │ ├── disqus.html │ ├── footer-adsense.html │ ├── fork-me.html │ ├── lazada-banner.html │ ├── license.html │ ├── money.html │ ├── page-header.html │ ├── posts.html │ ├── prevnext.html │ ├── prose.html │ ├── social-likes.html │ └── translation/ │ ├── eng/ │ │ ├── 00_intro.md │ │ ├── 01_what-exactly-is-a-large-javascript-application.md │ │ ├── 02_lets-review-your-current-architecture.md │ │ ├── 03_think-long-term.md │ │ ├── 04_brainstorming.md │ │ ├── 05_module-theory.md │ │ ├── 06_the-module-pattern.md │ │ ├── 07_object-literal-notation.md │ │ ├── 08_commonjs-modules.md │ │ ├── 09_the-facade-pattern.md │ │ ├── 10_the-mediator-pattern.md │ │ ├── 11_applying-the-facade-abstraction-of-the-core.md │ │ ├── 12_applying-the-mediator-the-application-core.md │ │ ├── 13_tying-it-all-together.md │ │ ├── 14_beyond-pub-sub-automatic-event-registration.md │ │ ├── 15_frequently-asked-questions.md │ │ └── 16_credits.md │ └── rus/ │ ├── 00_intro.md │ ├── 01_what-exactly-is-a-large-javascript-application.md │ ├── 02_lets-review-your-current-architecture.md │ ├── 03_think-long-term.md │ ├── 04_brainstorming.md │ ├── 05_module-theory.md │ ├── 06_the-module-pattern.md │ ├── 07_object-literal-notation.md │ ├── 08_commonjs-modules.md │ ├── 09_the-facade-pattern.md │ ├── 10_the-mediator-pattern.md │ ├── 11_applying-the-facade-abstraction-of-the-core.md │ ├── 12_applying-the-mediator-the-application-core.md │ ├── 13_tying-it-all-together.md │ ├── 14_beyond-pub-sub-automatic-event-registration.md │ ├── 15_frequently-asked-questions.md │ ├── 16_credits.md │ └── readme.md ├── _layouts/ │ └── page.html ├── _posts/ │ ├── 04-02-2014-the-mediator-pattern.md │ ├── 07-02-2014-applying-the-facade-abstraction-of-the-core.md │ ├── 09-01-2014-intro.md │ ├── 10-01-2014-what-exactly-is-a-large-javascript-application.md │ ├── 11-01-2014-lets-review-your-current-architecture.md │ ├── 11-02-2014-applying-the-mediator-the-application-core.md │ ├── 14-02-2014-tying-it-all-together.md │ ├── 15-01-2014-think-long-term.md │ ├── 16-01-2014-brainstorm.md │ ├── 17-01-2014-module-theory.md │ ├── 18-02-2014-beyond-pub-sub-automatic-event-registration.md │ ├── 19-02-2014-faq.md │ ├── 20-02-2014-credits.md │ ├── 21-01-2014-module-pattern.md │ ├── 24-01-2014-object-literal-notation.md │ ├── 28-01-2014-commonjs-modules.md │ └── 31-01-2014-the-facade-pattern.md ├── _stylus/ │ ├── _nib/ │ │ ├── Readme.md │ │ ├── iconic/ │ │ │ ├── demo.html │ │ │ ├── iconic.css │ │ │ └── iconic_stroke.otf │ │ ├── index.styl │ │ └── lib/ │ │ ├── nib/ │ │ │ ├── border.styl │ │ │ ├── clearfix.styl │ │ │ ├── color-image.styl │ │ │ ├── config.styl │ │ │ ├── flex.styl │ │ │ ├── gradients.styl │ │ │ ├── iconic.styl │ │ │ ├── image.styl │ │ │ ├── index.styl │ │ │ ├── overflow.styl │ │ │ ├── positions.styl │ │ │ ├── reset.styl │ │ │ ├── size.styl │ │ │ ├── text/ │ │ │ │ ├── aliases.styl │ │ │ │ ├── ellipsis.styl │ │ │ │ ├── hide-text.styl │ │ │ │ ├── index.styl │ │ │ │ └── replace-text.styl │ │ │ └── vendor.styl │ │ ├── nib.js │ │ └── nodes/ │ │ ├── color-image.js │ │ ├── gradient.js │ │ └── vendor-helpers.js │ └── main.styl ├── assets/ │ ├── css/ │ │ ├── main.css │ │ ├── print.css │ │ └── pygments.css │ ├── iconic/ │ │ └── iconic_stroke.otf │ └── vendor/ │ └── social-likes/ │ └── social-likes.css ├── atom.xml ├── epub/ │ ├── _source.md │ ├── cover.psd │ ├── largescale-js.epub │ ├── largescale-js.fb2 │ └── largescale-js.mobi ├── humans.txt └── index.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store node_modules _site test.js ================================================ FILE: Gemfile ================================================ source 'https://rubygems.org' gem 'github-pages' ================================================ FILE: LICENSE.md ================================================ This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/deed.en_US. ================================================ FILE: Makefile ================================================ build: stylus -o assets/css/ ./_stylus/main.styl serve: jekyll serve --watch --config _config-local.yml books: cat ./_includes/translation/rus/[0-9]*.md | sed 's///g' > ./epub/_source.md pandoc ./epub/_source.md -o ./epub/largescale-js.epub --toc-depth=2 --epub-cover-image=./epub/cover.jpg --epub-chapter-level=2 pandoc ./epub/_source.md -o ./epub/largescale-js.fb2 --toc-depth=2 --epub-cover-image=./epub/cover.jpg --epub-chapter-level=2 kindleGen -c1 -locale ru ./epub/largescale-js.epub -o largescale-js.mobi rm ./epub/_source.md .PHONY: build ================================================ FILE: README.md ================================================ # Паттерны для масштабируемых JavaScript-приложений Перевод на русский язык. Эдди Османи [в курсе][1]. ## Участники project : largescaleJS_ru repo age : 3 months active : 57 days commits : 405 files : 140 authors : 295 Shuvalov Anton 72.8% 45 Vladimir Starkov 11.1% 22 Rakhim Davletkaliyev 5.4% 10 andreyr82 2.5% 7 FMRobot 1.7% 7 Maxim Gladkih 1.7% 4 Nikita Bayev 1.0% 2 theghostbel 0.5% 2 Sergei Khaletskiy 0.5% 2 Vlad Tsepelev 0.5% 1 Mikhail Baranov 0.2% 1 Andrew 0.2% 1 Vladimir 0.2% 1 Artem Sapegin 0.2% 1 Andrey Rachkov 0.2% 1 croupier 0.2% 1 finico 0.2% 1 rakeev 0.2% 1 Vadim Makeev 0.2% ## Разработчикам Главы на английском языке и на русском лежат в директории *[./_includes/translation/][2]* В оформлении markdown мы стараемся придерживаться [правил][3] от Frontender Magazine. Локально сайт можно запустить примерно так: git clone https://github.com/shuvalov-anton/largescaleJS_ru.git largescalejs cd $_ bundle install stylus -w -o assets/css/ ./_stylus/main.styl jekyll serve --watch Версия для электронных книг, к сожалению, совершенно не зависит от других файлов. Собирать новые книги так: cd ./epub pandoc epub.md -o largescale-js.epub --toc-depth=2 --epub-cover-image=cover.jpg --epub-chapter-level=2 pandoc epub.md -o largescale-js.fb2 --toc-depth=2 --epub-cover-image=cover.jpg --epub-chapter-level=2 kindleGen -c1 -locale ru largescale-js.epub -o largescale-js.mobi [1]: https://twitter.com/addyosmani/status/415195066895171584 [2]: https://github.com/shuvalov-anton/Patterns-For-Large-Scale-JavaScript-Application-Architecture/tree/gh-pages/_includes/translation [3]: https://github.com/FMRobot/FM-guidelines ================================================ FILE: _config.yml ================================================ author: "Эдди Османи" title: "Паттерны для масштабируемых JavaScript-приложений" markdown: kramdown exclude: ['Gemfile', 'Gemfile.lock', 'makefile'] pygments: true production_url: http://largescalejs.ru baseurl: / permalink: /:title ================================================ FILE: _includes/adsense.html ================================================ ================================================ FILE: _includes/analytics.html ================================================ ================================================ FILE: _includes/disqus.html ================================================
comments powered by Disqus ================================================ FILE: _includes/footer-adsense.html ================================================ ================================================ FILE: _includes/fork-me.html ================================================ Fork me on GitHub ================================================ FILE: _includes/lazada-banner.html ================================================ ================================================ FILE: _includes/license.html ================================================

Creative Commons License

================================================ FILE: _includes/money.html ================================================ ================================================ FILE: _includes/page-header.html ================================================ ================================================ FILE: _includes/posts.html ================================================ ================================================ FILE: _includes/prevnext.html ================================================ {% if page.previous or page.next %}
{% if page.previous and page.previous.published %}
{% endif %} {% if page.next and page.next.published %}
{% endif %}
{% endif %} ================================================ FILE: _includes/prose.html ================================================ {% if page.index == true %} {% else %} Если вы заметили ошибку, вы всегда можете исправить ее с помощью prose.io. {% endif %} ================================================ FILE: _includes/social-likes.html ================================================ ================================================ FILE: _includes/translation/eng/00_intro.md ================================================ ### Today we're going to discuss an effective set of patterns for large-scale JavaScript application architecture. The material is based on my talk of the same name, last presented at LondonJS and inspired by[previous work][1] by Nicholas Zakas. ### Who am I and why am I writing about this topic? I'm currently a JavaScript and UI developer at AOL helping to plan and write the front-end architecture to our next generation of client-facing applications. As these applications are both complex and often require an architecture that is scalable and highly-reusable, it's one of my responsibilities to ensure the patterns used to implement such applications are as sustainable as possible. I also consider myself something of a design pattern enthusiast (although there are far more knowledgeable experts on this topic than I). I've previously written the creative-commons book '[Essential JavaScript Design Patterns][2]' and am in the middle of writing the more detailed follow up to this book at the moment. ### Can you summarize this article in 140 characters? In the event of you being short for time, here's the tweet-sized summary of this article: Decouple app. architecture w/module,facade & mediator patterns. Mods publish msgs, mediator acts as pub/sub mgr & facade handles security [1]: http://yuilibrary.com/theater/nicholas-zakas/zakas-architecture/ [2]: http://addyosmani.com/resources/essentialjsdesignpatterns/book/ ================================================ FILE: _includes/translation/eng/01_what-exactly-is-a-large-javascript-application.md ================================================ ### What exactly *is* a 'large' JavaScript application? Before we begin, let us attempt to define what we mean when we refer to a JavaScript application as being significantly 'large'. This is a question I've found still challenges developers with many years of experience in the field and the answer to this can be quite subjective. As an experiment, I asked a few intermediate developers to try providing their definition of it informally. One developer suggested 'a JavaScript application with over 100,000 LOC' whilst another suggested 'apps with over 1MB of JavaScript code written in-house'. Whilst valiant (if not scary) suggestions, both of these are**incorrect** as the size of a codebase does not always correlate to application complexity - those 100,000 LOC could easily represent quite trivial code. My own definition may or may not be universally accepted, but I believe that it 's closer to what a large application actually represents. In my view, large-scale JavaScript apps are **non-trivial** applications requiring**significant** developer effort to maintain, where most heavy lifting of data manipulation and display falls to the**browser**. The last part of this definition is possibly the most significant. ================================================ FILE: _includes/translation/eng/02_lets-review-your-current-architecture.md ================================================ ### Let's review your current architecture. If working on a significantly large JavaScript application, remember to dedicate**sufficient time** to planning the underlying architecture that makes the most sense. It's often more complex than you may initially imagine. I can't stress the importance of this enough - some developers I've seen approach larger applications have stepped back and said 'Okay. Well, there are a set of ideas and patterns that worked well for me on my last medium-scale project. Surely they should mostly apply to something a little larger, right?'. Whilst this may be true to an extent, please don't take it for granted - **larger apps generally have greater concerns that need to be factored in**. I'm going to discuss shortly why spending a little more time planning out the structure to your application is worth it in the long run. Most JavaScript developers likely use a mixed combination of the following for their current architecture: * custom widgets * models * views * controllers * templates * libraries/toolkits * an application core. You probably also break down your application's functionality into blocks of modules or apply other patterns for this. This is great, but there are a number of potential problems you can run into if this represents all of your application's structure. ##### 1. How much of this architecture is instantly re-usable? Can single modules exist on their own independently? Are they self-contained? Right now if I were to look at the codebase for a large application you or your team were working on and selected a random module, would it be possible for me to easily just drop it into a new page and start using it on its own?. You may question the rationale behind wanting to do this, however I encourage you to think about the future. What if your company were to begin building more and more non-trivial applications which shared some cross-over in functionality?. If someone said, 'Our users love using the chat module in our mail client. Let's drop that into our new collaborative editing suite', would this be possible without significantly altering the code? ##### 2. How much do modules depend on other modules in the system? Are they tightly coupled? Before I dig into why this is a concern, I should note that I understand it's not always possible to have modules with absolutely no other dependencies in a system. At a granular level you may well have modules that extend the base functionality of others, but this question is more-so related to groups of modules with distinct functionality. It should be possible for all of these distinct sets of modules to work in your application without depending on too many other modules being present or loaded in order to function. ##### 3. If specific parts of your application fail, can it still function? If you're building a GMail-like application and your webmail module (or modules ) fail, this shouldn't block the rest of the UI or prevent users from being able to use other parts of the page such as chat. At the same time, as per before, modules should ideally be able to exist on their own outside of your current application architecture. In my talks I mention dynamic dependency (or module) loading based on expressed user-intent as something related. For example, in GMail's case they might have the chat module collapsed by default without the core module code loaded on page initialization. If a user expressed an intent to use the chat feature, only then would it be dynamically loaded. Ideally, you want this to be possible without it negatively affecting the rest of your application. ##### 4. How easily can you test individual modules? When working on systems of significant scale where there's a potential for millions of users to use (or mis-use) the different parts it, it's essential that modules which may end up being re-used across a number of different applications be sufficiently tested. Testing needs to be possible for when the module both inside and outside of the architecture for which it was initially built. In my view, this provides the most assurance that it shouldn't break if dropped into another system. ================================================ FILE: _includes/translation/eng/03_think-long-term.md ================================================ ### Think Long Term When devising the architecture for your large application, it's important to think ahead. Not just a month or a year from now, but beyond that. What might change? It's of course impossible to guess exactly how your application may grow, but there's certainly room to consider what is likely. Here, there is at least one specific aspect of your application that comes to mind. Developers often couple their DOM manipulation code quite tightly with the rest of their application - even when they've gone to the trouble of separating their core logic down into modules. Think about it..why is this not a good idea if we' re thinking long-term? One member of my audience suggested that it was because a rigid architecture defined in the present may not be suitable for the future. Whilst certainly true, there's another concern that may cost even more if not factored in. You may well decide to **switch** from using Dojo, jQuery, Zepto or YUI to something entirely different for reasons of performance, security or design in the future. This can become a problem because libraries are not easily interchangeable and have high switching costs if tightly coupled to your app. If you're a Dojo developer (like some of the audience at my talk), you may not have something better to switch to in the present, but who is to say that in 2-3 years something better doesn't come out that you'll want to switch to? . This is a relatively trivial decision in smaller codebases but for larger applications, having an architecture which is flexible enough to support **not** caring about the libraries being used in your modules can be of great benefit, both financially and from a time-saving perspective. To summarize, if you reviewed your architecture right now, could a decision to switch libraries be made without rewriting your entire application?. If not, consider reading on because I think the architecture being outlined today may be of interest. There are a number of influential JavaScript developers who have previously outlined some of the concerns I've touched upon so far. Three key quotes I would like to share from them are the following: "The secret to building large apps is never build large apps. Break your applications into small pieces. Then, assemble those testable, bite-sized pieces into your big application " - **Justin Meyer, author JavaScriptMVC** "The key is to acknowledge from the start that you have no idea how this will grow. When you accept that you don't know everything, you begin to design the system defensively. You identify the key areas that may change, which often is very easy when you put a little bit of time into it. For instance, you should expect that any part of the app that communicates with another system will likely change, so you need to abstract that away. " - **Nicholas Zakas, author 'High-performance JavaScript websites'** and last but not least: "The more tied components are to each other, the less reusable they will be, and the more difficult it becomes to make changes to one without accidentally affecting another " - **Rebecca Murphey, author of jQuery Fundamentals.** These principles are essential to building an architecture that can stand the test of time and should always be kept in mind. ================================================ FILE: _includes/translation/eng/04_brainstorming.md ================================================ ### Brainstorming Let's think about what we're trying to achieve for a moment. We want a loosely coupled architecture with functionality broken down into ** independent modules** with ideally no inter-module dependencies. Modules ** speak** to the rest of the application when something interesting happens and an**intermediate layer** interprets and reacts to these messages. For example, if we had a JavaScript application responsible for an online bakery, one such 'interesting' message from a module might be 'batch 42 of bread rolls is ready for dispatch '. We use a different layer to interpret messages from modules so that a) modules don't directly access the core and b) modules don't need to directly call or interact with other modules. This helps prevent applications from falling over due to errors with specific modules and provides us a way to kick-start modules which have fallen over. Another concern is security. The reality is that most of us don't consider internal application security as that much of a concern. We tell ourselves that as we're structuring the application, we're intelligent enough to figure out what should be publicly or privately accessible. However, wouldn't it help if you had a way to determine what a module was permitted to do in the system? eg. if I know I've limited the permissions in my system to not allow a public chat widget to interface with an admin module or a module with DB-write permissions, I can limit the chances of someone exploiting vulnerabilities I have yet to find in the widget to pass some XSS in there. Modules shouldn’t be able to access everything. They probably can in most current architectures, but do they really need to be able to? Having an intermediate layer handle permissions for which modules can access which parts of your framework gives you added security. This means a module is only able to do at most what we’ve permitted it do. ## The Proposed Architecture The solution to the architecture we seek to define is a combination of three well-known design patterns: the**module**, **facade** and **mediator**. Rather than the traditional model of modules directly communicating with each other, in this decoupled architecture, they'll instead only publish events of interest (ideally, without a knowledge of other modules in the system). The mediator pattern will be used to both subscribe to messages from these modules and handle what the appropriate response to notifications should be. The facade pattern will be used to enforce module permissions. I will be going into more detail on each of these patterns below: ================================================ FILE: _includes/translation/eng/05_module-theory.md ================================================ ### Module Theory You probably already use some variation of modules in your existing architecture. If however, you don't, this section will present a short primer on them. Modules are an **integral** piece of any robust application's architecture and are typically single-purpose parts of a larger system that are interchangeable. Depending on how you implement modules, it's possible to define dependencies for them which can be automatically loaded to bring together all of the other parts instantly. This is considered more scalable than having to track the various dependencies for them and manually load modules or inject script tags. Any significantly non-trivial application should be built from modular components. Going back to GMail, you could consider modules independent units of functionality that can exist on their own, so the chat feature for example. It's probably backed by a chat module, however, depending on how complex that unit of functionality is, it may well have more granular sub-modules that it depends on. For example, one could have a module simply to handle the use of emoticons which can be shared across both chat and mail composition parts of the system. In the architecture being discussed, modules have a **very limited knowledge** of what's going on in the rest of the system. Instead, we delegate this responsibility to a mediator via a facade. This is by design because if a module only cares about letting the system know when something of interest happens without worrying if other modules are running, a system is capable of supporting adding, removing or replacing modules without the rest of the modules in the system falling over due to tight coupling. Loose coupling is thus essential to this idea being possible. It facilitates easier maintainability of modules by removing code dependencies where possible. In our case, modules should not rely on other modules in order to function correctly. When loose coupling is implemented effectively, its straight-forward to see how changes to one part of a system may affect another. In JavaScript, there are several options for implementing modules including the well-known module pattern and object literals. Experienced developers will already be familiar with these and if so, please skip ahead to the section on CommonJS modules. ================================================ FILE: _includes/translation/eng/06_the-module-pattern.md ================================================ ##### The Module Pattern The module pattern is a popular design that pattern that encapsulates 'privacy', state and organization using closures. It provides a way of wrapping a mix of public and private methods and variables, protecting pieces from leaking into the global scope and accidentally colliding with another developer's interface. With this pattern, only a public API is returned, keeping everything else within the closure private. This provides a clean solution for shielding logic doing the heavy lifting whilst only exposing an interface you wish other parts of your application to use. The pattern is quite similar to an immediately-invoked functional expression ([IIFE][3]) except that an object is returned rather than a function. It should be noted that there isn't really a true sense of 'privacy' inside JavaScript because unlike some traditional languages, it doesn't have access modifiers. Variables can't technically be declared as being public nor private and so we use function scope to simulate this concept. Within the module pattern, variables or methods declared are only available inside the module itself thanks to closure. Variables or methods defined within the returning object however are available to everyone. Below you can see an example of a shopping basket implemented using the pattern . The module itself is completely self-contained in a global object called `basketModule`. The `basket` array in the module is kept private and so other parts of your application are unable to directly read it. It only exists with the module's closure and so the only methods able to access it are those with access to its scope (ie.`addItem()`, `getItem()` etc). var basketModule = (function() { var basket = []; //private return { //exposed to public addItem: function(values) { basket.push(values); }, getItemCount: function() { return basket.length; }, getTotal: function(){ var q = this.getItemCount(),p=0; while(q--){ p+= basket[q].price; } return p; } } }()); Inside the module, you'll notice we return an `object`. This gets automatically assigned to`basketModule` so that you can interact with it as follows: //basketModule is an object with properties which can also be methods basketModule.addItem({item:'bread',price:0.5}); basketModule.addItem({item:'butter',price:0.3}); console.log(basketModule.getItemCount()); console.log(basketModule.getTotal()); //however, the following will not work: console.log(basketModule.basket);// (undefined as not inside the returned object) console.log(basket); //(only exists within the scope of the closure) The methods above are effectively namespaced inside `basketModule`. From a historical perspective, the module pattern was originally developed by a number of people including[Richard Cornford][4] in 2003. It was later popularized by Douglas Crockford in his lectures and re-introduced by Eric Miraglia on the YUI blog. How about the module pattern in specific toolkits or frameworks? **Dojo** Dojo attempts to provide 'class'-like functionality through `dojo.declare`, which can be used for amongst other things, creating implementations of the module pattern. For example, if we wanted to declare`basket` as a module of the `store` namespace, this could be achieved as follows: //traditional way var store = window.store || {}; store.basket = store.basket || {}; //using dojo.setObject dojo.setObject("store.basket.object", (function() { var basket = []; function privateMethod() { console.log(basket); } return { publicMethod: function(){ privateMethod(); } }; }())); which can become quite powerful when used with `dojo.provide` and mixins. ** YUI ** The following example is heavily based on the original YUI module pattern implementation by Eric Miraglia, but is relatively self-explanatory. YAHOO.store.basket = function () { //"private" variables: var myPrivateVar = "I can be accessed only within YAHOO.store.basket ."; //"private" method: var myPrivateMethod = function () { YAHOO.log("I can be accessed only from within YAHOO.store.basket"); } return { myPublicProperty: "I'm a public property.", myPublicMethod: function () { YAHOO.log("I'm a public method."); //Within basket, I can access "private" vars and methods: YAHOO.log(myPrivateVar); YAHOO.log(myPrivateMethod()); //The native scope of myPublicMethod is store so we can //access public members using "this": YAHOO.log(this.myPublicProperty); } }; }(); ** jQuery ** There are a number of ways in which jQuery code unspecific to plugins can be wrapped inside the module pattern. Ben Cherry previously suggested an implementation where a function wrapper is used around module definitions in the event of there being a number of commonalities between modules. In the following example, a `library` function is defined which declares a new library and automatically binds up the`init` function to `document.ready` when new libraries (ie. modules) are created. function library(module) { $(function() { if (module.init) { module.init(); } }); return module; } var myLibrary = library(function() { return { init: function() { /*implementation*/ } }; }()); ================================================ FILE: _includes/translation/eng/07_object-literal-notation.md ================================================ ##### Object Literal Notation In object literal notation, an object is described as a set of comma-separated name/value pairs enclosured in curly braces (`{}`). Names inside the object may be either strings or identifiers that are followed by a colon. There should be no comma used after the final name/value pair in the object as this may result in errors. Object literals don't require instantiation using the `new` operator but shouldn't be used at the start of a statement as the opening`{` may be interpreted as the beginning of a block. Below you can see an example of a module defined using object literal syntax. New members may be added to the object using assignment as follows`myModule.property = 'someValue';` Whilst the module pattern is useful for many things, if you find yourself not requiring specific properties or methods to be private, the object literal is a more than suitable alternative. var myModule = {     myProperty : 'someValue',     //object literals can contain properties and methods.     //here, another object is defined for configuration     //purposes:     myConfig:{         useCaching:true,         language: 'en'        },     //a very basic method     myMethod: function(){         console.log('I can haz functionality?');     },     //output a value based on current configuration     myMethod2: function(){         console.log('Caching is:' + (this.myConfig.useCaching)?'enabled':'disabled');     },     //override the current configuration     myMethod3: function(newConfig){         if(typeof newConfig == 'object'){            this.myConfig = newConfig;            console.log(this.myConfig.language);         }     } }; myModule.myMethod(); //I can haz functionality myModule.myMethod2(); //outputs enabled myModule.myMethod3({language:'fr',useCaching:false}); //fr [3]: http://benalman.com/news/2010/11/immediately-invoked-function-expression/ [4]: http://groups.google.com/group/comp.lang.javascript/msg/9f58bd11bd67d937 ================================================ FILE: _includes/translation/eng/08_commonjs-modules.md ================================================ ### CommonJS Modules Over the last year or two, you may have heard about [CommonJS][5] - a volunteer working group which designs, prototypes and standardizes JavaScript APIs. To date they've ratified standards for modules and packages.The CommonJS AMD proposal specifies a simple API for declaring modules which can be used with both synchronous and asynchronous script tag loading in the browser. Their module pattern is relatively clean and I consider it a reliable stepping stone to the module system proposed for ES Harmony (the next release of the JavaScript language ). From a structure perspective, a CommonJS module is a reusable piece of JavaScript which exports specific objects made available to any dependent code. This module format is becoming quite ubiquitous as a standard module format for JS. There are plenty of great tutorials on implementing CommonJS modules, but at a high-level they basically contain two primary parts: an`exports` object that contains the objects a module wishes to make available to other modules and a `require` function that modules can use to import the exports of other modules. /* Example of achieving compatibility with AMD and standard CommonJS by putting boilerplate around the standard CommonJS module format: */ (function(define){ define(function(require,exports){ // module contents var dep1 = require("dep1"); exports.someExportedFunction = function(){...}; //... }); })(typeof define=="function"?define:function(factory){factory(require,exports)}); There are a number of great JavaScript libraries for handling module loading in the**CommonJS** module format, but my personal preference is RequireJS. A complete tutorial on RequireJS is outside the scope of this tutorial, but I can recommend reading James Burke's ScriptJunkie post on it[here][6]. I know a number of people that also like Yabble. Out of the box, RequireJS provides methods for easing how we create static modules with wrappers and it's extremely easy to craft modules with support for asynchronous loading. It can easily load modules and their dependencies this way and execute the body of the module once available. There are some developers that however claim CommonJS modules aren't suitable enough for the browser. The reason cited is that they can't be loaded via a script tag without some level of server-side assistance. We can imagine having a library for encoding images as ASCII art which might export a`encodeToASCII` function. A module from this could resemble: var encodeToASCII = require("encoder").encodeToASCII; exports.encodeSomeSource = function(){ //process then call encodeToASCII } This type of scenario wouldn't work with a script tag because the scope isn't wrapped, meaning our`encodeToASCII` method would be attached to the `window` `require` wouldn't be as such defined and exports would need to be created for each module separately. A client-side library together with server-side assistance or a library loading the script with an XHR request using`eval()` could however handle this easily. Using RequireJS, the module from earlier could be rewritten as follows: define(function(require, exports, module) { var encodeToASCII = require("encoder").encodeToASCII; exports.encodeSomeSource = function(){ //process then call encodeToASCII } }); For developers who may not rely on just using static JavaScript for their projects, CommonJS modules are an excellent path to go down, but do spend some time reading up on it. I've really only covered the tip of the ice berg but both the CommonJS wiki and Sitepen have a number of resources if you wish to read further. [5]: http://commonjs.org [6]: http://msdn.microsoft.com/en-us/scriptjunkie/ff943568 ================================================ FILE: _includes/translation/eng/09_the-facade-pattern.md ================================================ ### The Facade Pattern Next, we're going to look at the facade pattern, a design pattern which plays a critical role in the architecture being defined today. When you put up a facade, you're usually creating an outward appearance which conceals a different reality. The facade pattern provides a convenient**higher- level interface** to a larger body of code, hiding its true underlying complexity. Think of it as simplifying the API being presented to other developers. Facades are a **structural pattern** which can often be seen in JavaScript libraries and frameworks where, although an implementation may support methods with a wide range of behaviors, only a 'facade' or limited abstract of these methods is presented to the client for use. This allows us to interact with the facade rather than the subsystem behind the scenes. The reason the facade is of interest is because of its ability to hide implementation-specific details about a body of functionality contained in individual modules. The implementation of a module can change without the clients really even knowing about it. By maintaining a consistent facade (simplified API), the worry about whether a module extensively uses dojo, jQuery, YUI, zepto or something else becomes significantly less important. As long as the interaction layer doesn't change, you retain the ability to switch out libraries (eg. jQuery for Dojo) at a later point without affecting the rest of the system. Below is a very basic example of a facade in action. As you can see, our module contains a number of methods which have been privately defined. A facade is then used to supply a much simpler API to accessing these methods: var module = (function() {     var _private = {         i:5,         get : function() {             console.log('current value:' + this.i);         },         set : function( val ) {             this.i = val;         },         run : function() {             console.log('running');         }, jump: function(){ console.log('jumping'); }     };     return {         facade : function( args ) {             _private.set(args.val);             _private.get();             if ( args.run ) {                 _private.run();             }         }     } }()); module.facade({run: true, val:10}); //outputs current value: 10, running and that's really it for the facade before we apply it to our architecture. Next, we'll be diving into the exciting mediator pattern. The core difference between the facade pattern and the mediator is that the facade (a structural pattern) only exposes existing functionality whilst the mediator (a behavioral pattern) can add functionality. ================================================ FILE: _includes/translation/eng/10_the-mediator-pattern.md ================================================ ### The Mediator Pattern The mediator pattern is best introduced with a simple analogy - think of your typical airport traffic control. The tower handles what planes can take off and land because all communications are done from the planes to the control tower, rather than from plane-to-plane. A centralized controller is key to the success of this system and that's really what a mediator is. Mediators are used when the communication between modules may be complex, but is still**well defined**. If it appears a system may have too many relationships between modules in your code, it may be time to have a central point of control, which is where the pattern fits in. In real-world terms, a mediator** encapsulates** how disparate modules ** interact** with each other by acting as an intermediary. The pattern also promotes loose coupling by preventing objects from referring to each other explicitly - in our system, this helps to solve our module inter-dependency issues. What other advantages does it have to offer? Well, mediators allow for actions of each module to vary independently, so it’s extremely flexible. If you've previously used the Observer (Pub/Sub) pattern to implement an event broadcast system between the modules in your system, you'll find mediators relatively easy to understand. Let's take a look at a high level view of how modules might interact with a mediator: ![][7] Consider modules as publishers and the mediator as both a publisher and subscriber. Module 1 broadcasts an event notifying the mediator something needs to done. The mediator captures this message and 'starts' the modules needed to complete this task Module 2 performs the task that Module 1 requires and broadcasts a completion event back to the mediator. In the mean time, Module 3 has also been started by the mediator and is logging results of any notifications passed back from the mediator. Notice how at no point do any of the modules** directly communicate** with one another. If Module 3 in the chain were to simply fail or stop functioning, the mediator could hypothetically 'pause' the tasks on the other modules, stop and restart Module 3 and then continue working with little to no impact on the system. This level of decoupling is one of the main strengths the pattern has to offer. To review, the advantages of the mediator are that: It decouples modules by introducing an intermediary as a central point of control.It allows modules to broadcast or listen for messages without being concerned with the rest of the system. Messages can be handled by any number of modules at once. It is typically significantly more easy to add or remove features to systems which are loosely coupled like this. And its disadvantages: By adding a mediator between modules, they must always communicate indirectly. This can cause a very minor performance drop - because of the nature of loose coupling, its difficult to establish how a system might react by only looking at the broadcasts. At the end of the day, tight coupling causes all kinds of headaches and this is one solution. ** Example:** This is a possible implementation of the mediator pattern based on previous work by[@rpflorence][8] var mediator = (function(){     var subscribe = function(channel, fn){         if (!mediator.channels[channel]) mediator.channels[channel] = [];         mediator.channels[channel].push({ context: this, callback: fn });         return this;     },     publish = function(channel){         if (!mediator.channels[channel]) return false;         var args = Array.prototype.slice.call(arguments, 1);         for (var i = 0, l = mediator.channels[channel].length; i **Example:** Here are two sample uses of the implementation from above. It's effectively managed publish/subscribe: //Pub/sub on a centralized mediator mediator.name = "tim"; mediator.subscribe('nameChange', function(arg){         console.log(this.name);         this.name = arg;         console.log(this.name); }); mediator.publish('nameChange', 'david'); //tim, david //Pub/sub via third party mediator var obj = { name: 'sam' }; mediator.installTo(obj); obj.subscribe('nameChange', function(arg){         console.log(this.name);         this.name = arg;         console.log(this.name); }); obj.publish('nameChange', 'john'); //sam, john [7]: img/chart4a.jpg [8]: https://github.com/rpflorence ================================================ FILE: _includes/translation/eng/11_applying-the-facade-abstraction-of-the-core.md ================================================ ### **Applying The Facade: Abstraction Of The Core** In the architecture suggested: A facade serves as an **abstraction** of the application core which sits between the mediator and our modules - it should ideally be the **only** other part of the system modules are aware of. The responsibilities of the abstraction include ensuring a **consistent interface** to these modules is available at all times. This closely resembles the role of the **sandbox controller** in the excellent architecture first suggested by Nicholas Zakas. Components are going to communicate with the mediator through the facade so it needs to be **dependable**. When I say 'communicate', I should clarify that as the facade is an abstraction of the mediator which will be listening out for broadcasts from modules that will be relayed back to the mediator. In addition to providing an interface to modules, the facade also acts as a security guard, determining which parts of the application a module may access. Components only call **their own** methods and shouldn't be able to interface with anything they don't have permission to. For example, a module may broadcast dataValidationCompletedWriteToDB. The idea of a security check here is to ensure that the module has permissions to request database-write access. What we ideally want to avoid are issues with modules accidentally trying to do something they shouldn't be. To review in short, the mediator remains a type of pub/sub manager but is only passed interesting messages once they've cleared permission checks by the facade. ================================================ FILE: _includes/translation/eng/12_applying-the-mediator-the-application-core.md ================================================ ### **Applying the Mediator: The Application Core** The mediator plays the role of the application core. We've briefly touched on some of its responsibilities but lets clarify what they are in full. The core's primary job is to manage the module **lifecycle. **When the core detects an** interesting message** it needs to decide how the application should react - this effectively means deciding whether a module or set of modules needs to be **started** or** stopped**. Once a module has been started, it should ideally execute **automatically**. It's not the core's task to decide whether this should be when the DOM is ready and there's enough scope in the architecture for modules to make such decisions on their own. You may be wondering in what circumstance a module might need to be 'stopped' - if the application detects that a particular module has failed or is experiencing significant errors, a decision can be made to prevent methods in that module from executing further so that it may be restarted. The goal here is to assist in reducing disruption to the user experience. In addition, the core should enable **adding or removing** modules without breaking anything. A typical example of where this may be the case is functionality which may not be available on initial page load, but is dynamically loaded based on expressed user-intent eg. going back to our GMail example, Google could keep the chat widget collapsed by default and only dynamically load in the chat module(s) when a user expresses an interest in using that part of the application. From a performance optimization perspective, this may make sense. Error management will also be handled by the application core. In addition to modules broadcasting messages of interest they will also broadcast any errors experienced which the core can then react to accordingly (eg. stopping modules, restarting them etc).It's important that as part of a decoupled architecture there to be enough scope for the introduction of new or better ways of handling or displaying errors to the end user without manually having to change each module. Using publish/subscribe through a mediator allows us to achieve this. ================================================ FILE: _includes/translation/eng/13_tying-it-all-together.md ================================================ ### **Tying It All Together** * **Modules** contain specific pieces of functionality for your application. They publish notifications informing the application whenever something interesting happens - this is their primary concern. As I'll cover in the FAQs, modules can depend on DOM utility methods, but ideally shouldn't depend on any other modules in the system. They should not be concerned with: * what objects or modules are subscribing to the messages they publish * where these objects are based (whether this is on the client or server ) * how many objects subscribe to notifications **![][9]** * **The Facade** abstracts the core to avoid modules touching it directly. It subscribes to interesting events (from modules) and says 'Great! What happened? Give me the details!'. It also handles module security by checking to ensure the module broadcasting an event has the necessary permissions to pass such events that can be accepted. **![][10]** * **The Mediator (Application Core)** acts as a 'Pub/Sub' manager using the mediator pattern. It's responsible for module management and starts/stops modules as needed. This is of particular use for dynamic dependency loading and ensuring modules which fail can be centrally restarted as needed. **![][11]** The result of this architecture is that modules (in most cases) are theoretically no longer dependent on other modules. They can be easily tested and maintained on their own and because of the level of decoupling applied, modules can be picked up and dropped into a new page for use in another project without significant additional effort. They can also be dynamically added or removed without the application falling over. [9]: img/chart1a.gif [10]: img/chart2a.gif [11]: img/chart3a.gif ================================================ FILE: _includes/translation/eng/14_beyond-pub-sub-automatic-event-registration.md ================================================ ### Beyond Pub/Sub: Automatic Event Registration As previously mentioned by Michael Mahemoff, when thinking about large-scale JavaScript, it can be of benefit to exploit some of the more dynamic features of the language. You can read more about some of the concerns highlighted on Michael's[G+][12] page, but I would like to focus on one specifically - automatic event registration (AER ). AER solves the problem of wiring up subscribers to publishers by introducing a pattern which auto-wires based on naming conventions. For example, if a module publishes an event called`messageUpdate`, anything with a `messageUpdate` method would be automatically called. The setup for this pattern involves registering all components which might subscribe to events, registering all events that may be subscribed to and finally for each subscription method in your component-set, binding the event to it. It's a very interesting approach which is related to the architecture presented in this post, but does come with some interesting challenges. For example, when working dynamically, objects may be required to register themselves upon creation. Please feel free to check out Michael's[post][13] on AER as he discusses how to handle such issues in more depth. [12]: https://plus.google.com/106413090159067280619/posts/hDZkVrDXZR6 [13]: http://softwareas.com/automagic-event-registration ================================================ FILE: _includes/translation/eng/15_frequently-asked-questions.md ================================================ ### Frequently Asked Questions #### Q: Is it possible to avoid implementing a sandbox or facade altogether? A: Although the architecture outlined uses a facade to implement security features, it's entirely possible to get by using a mediator and pub/sub to communicate events of interest throughout the application without it. This lighter version would offer a similar level of decoupling, but ensure you're comfortable with modules directly touching the application core (mediator) if opting for this variation. #### Q: You've mentioned modules not having any dependencies. Does this include dependencies such as third party libraries (eg. jQuery? ) A: I'm specifically referring to dependencies on other modules here. What some developers opting for an architecture such as this opt for is actually abstracting utilities common to DOM libraries -eg. one could have a DOM utility class for query selectors which when used returns the result of querying the DOM using jQuery (or, if you switched it out at a later point, Dojo). This way, although modules still query the DOM, they aren't directly using hardcoded functions from any particular library or toolkit. There's quite a lot of variation in how this might be achieved, but the takeaway is that ideally core modules shouldn't depend on other modules if opting for this architecture. You'll find that when this is the case it can sometimes be more easy to get a complete module from one project working in another with little extra effort. I should make it clear that I fully agree that it can sometimes be significantly more sensible for modules to extend or use other modules for part of their functionality, however bear in mind that this can in some cases increase the effort required to make such modules 'liftable' for other projects. #### Q: I'd like to start using this architecture today. Is there any boilerplate code around I can work from? A: I plan on releasing a free boilerplate pack for this post when time permits , but at the moment, your best bet is probably the '[Writing Modular JavaScript][14]' premium tutorial by Andrew Burgees (for complete disclosure, this is a referral link as any credits received are re- invested into reviewing material before I recommend it to others). Andrew's pack includes a screencast and code and covers most of the main concepts outlined in this post but opts for calling the facade a 'sandbox', as per Zakas. There's some discussion regarding just how DOM library abstraction should be ideally implemented in such an architecture - similar to my answer for the second question, Andrew opts for some interesting patterns on generalizing query selectors so that at most, switching libraries is a change that can be made in a few short lines. I'm not saying this is the right or best way to go about this, but it's an approach I personally also use. #### Q: If the modules need to directly communicate with the core, is this possible? A: As Zakas has previously hinted, there's technically no reason why modules shouldn't be able to access the core but this is more of a best practice than anything. If you want to strictly stick to this architecture you'll need to follow the rules defined or opt for a looser architecture as per the answer to the first question. [14]: http://bit.ly/orGVOL ================================================ FILE: _includes/translation/eng/16_credits.md ================================================ ### Credits Thanks to Nicholas Zakas for his original work in bringing together many of the concepts presented today; Andrée Hansson for his kind offer to do a technical review of the post (as well as his feedback that helped improve it); Rebecca Murphey, Justin Meyer, John Hann, Peter Michaux, Paul Irish and Alex Sexton, all of whom have written material related to the topics discussed in the past and are a constant source of inspiration for both myself and others. ================================================ FILE: _includes/translation/rus/00_intro.md ================================================ В этой книге мы обсудим набор паттернов, который поможет вам в создании больших масштабируемых JavaScript-приложений. Материал книги основан на моем одноименном докладе, впервые прочитанном на конференции «LondonJS», и вдохновленном [предшествующей ему работой][1] Николаса Закаса. ### Кто я и почему я решил об этом написать? Меня зовут Эдди Османи. Сейчас я работаю JavaScript- и UI-разработчиком в AOL. Я занимаюсь планированием и написанием фронтенд-архитектуры для следующего поколения наших пользовательских приложений. Эти приложения весьма сложны. Они нуждаются в архитектуре, позволяющей, с одной стороны легко их масштабировать, а с другой достаточно легко использовать повторно их модули. Также я занимаюсь разработкой шаблонов, которые можно применять в разработке приложений подобного масштаба настолько качественно, насколько это вообще возможно. Кроме того, я рассматриваю себя как евангелиста шаблонов проектирования (хотя есть много экспертов, разбирающихся в этом лучше меня). В прошлом я написал книгу «[Essential JavaScript Design Patterns][2]», а сейчас я занимаюсь написанием более подробного продолжения этой книги. ### Могу ли я уместить эту книгу в 140 символов? Я уместил эту статью в один твит, на случай, если у вас совсем мало времени: Меньше связанности: используйте паттерны «модуль», «фасад» и «медиатор». Модули общаются через медиатор, а фасад обеспечивает безопасность. {:class="message"} [1]: http://www.slideshare.net/nzakas/scalable-javascript-application-architecture [2]: http://addyosmani.com/resources/essentialjsdesignpatterns/book/ ================================================ FILE: _includes/translation/rus/01_what-exactly-is-a-large-javascript-application.md ================================================ Перед тем как я начну, давайте постараемся определить, что именно мы имеем в виду, когда говорим о больших JavaScript-приложениях. Этот вопрос я считаю своего рода вызовом опытным разработчикам, и ответы на него, соответственно, получаются очень субъективными. Ради эксперимента я предложил нескольким среднестатистическим разработчикам дать собственное определение этому термину. Один из разработчиков сказал, что речь идет о «JavaScript-приложениях, состоящих из более чем 100 000 строк кода», когда другой определил что большое приложение «содержит больше чем 1 МБ JavaScript». Я расстроил храбрецов — оба этих варианта далеки от истины. Количество кода не всегда коррелирует со сложностью приложения. 100 000 строк легко могут оказаться самым ничем не примечательным простым кодом. Я не знаю, подходит ли мое собственное определение к любому случаю, но я верю, что оно находится ближе всего к тому, что действительно представляет из себя большое JavaScript-приложение. {:class="message"} Я думаю, что большие JavaScript-приложения решают нетривиальные задачи, а поддержка таких приложений требует от разработчика серьезных усилий. При этом, большая часть работы по манипуляции данными и их отображению ложится на браузер. Я думаю, что последняя часть определения — самая важная. ================================================ FILE: _includes/translation/rus/02_lets-review-your-current-architecture.md ================================================ {:.message} Работая над большим JavaScript-приложением, не забывайте уделять **достаточное количество времени** на планирование изначальной архитектуры, к которой такие приложения очень чувствительны. Большие приложения обычно представляют из себя очень сложные системы, гораздо более сложные, чем вы представляете себе изначально. Я должен подчеркнуть значение этой разницы — я видел разработчиков, которые, сталкиваясь с большими приложениями, делали шаг назад и говорили: «Хорошо, у меня есть несколько идей, которые хорошо показали себя в моем предыдущем проекте среднего масштаба. Думаю, они точно сработают и для чего-то большего, не так ли?». Конечно, до какого-то момента это может быть так, но, пожалуйста, не принимайте это как должное — по большей части большие приложения имеют ряд достаточно серьезных проблем, с которыми нужно считаться. Ниже я приведу несколько доводов в пользу того, почему вам стоит уделить немного больше времени планированию архитектуры своего приложения, и чем вам это будет полезно в долгосрочной перспективе. Большинство JavaScript-разработчиков в архитектуре своих приложений обычно использует различные комбинации следующих компонентов: * виджеты * модели * представления * контроллеры * шаблоны * библиотеки * ядро приложения. {:class="message"} **Ссылки по теме:** [Ребекка Мёрфи — Создание архитектуры JavaScript-приложений][1] [Питер Мишо — MVC архитектура для JavaScript-приложений][2] [StackOverflow — Дискуссия о современных MVC-фреймворках][3] [Дуг Найнер — Поддерживаемые плагины и фабрика виджетов][4] Вероятно, вы выносите различные функции ваших приложений в отдельные модули, либо используете какие-нибудь другие шаблоны проектирования для подобного разделения. Это очень хорошо, но здесь есть ряд потенциальных проблем, с которыми вы можете столкнуться при таком подходе. ### 1. Готова ли ваша архитектура к повторному использованию кода уже сейчас? Могут ли отдельные модули использоваться самостоятельно? Достаточно ли они автономны для этого? Мог бы я прямо сейчас взять один из модулей вашего большого приложения, просто поместить его на новую веб-страницу, а затем, тут же, начать его использовать? У вас может возникнуть вопрос: «Действительно ли это так необходимо?», но, как бы то ни было, я надеюсь, что вы думаете о будущем. Что, если ваша компания начнет создавать все больше и больше нетривиальных приложений, которые будут иметь некоторую общую функциональность? Если кто-то скажет: «Нашим пользователям очень нравится использовать модуль чата в нашем email-клиенте, почему бы нам не добавить этот модуль к нашему новому приложению для совместной работы с документами?», будет ли это возможно, если мы не уделим должного внимания контролю кода? ### 2. Сколько модулей в вашей системе зависит от других модулей? Насколько сильно связаны ваши модули? Перед тем, как я погружусь в объяснения о том, как важна слабая связанность модулей, я должен отметить, что не всегда есть возможность создавать модули, не имеющие абсолютно никаких зависимостей в системе. К примеру, одни модули могут расширять функции других, уже существующих. Эта тема, скорее всего, относится к группировке модулей на основе некоторой функциональности. Отдельные наборы модулей должны работать в вашем приложении без большого количества зависимостей, чтобы наличие или загрузка других модулей не влияла на их работоспособность. ### 3. Сможет ли ваше приложение работать дальше, если его отдельная часть сломается? Если вы разрабатываете приложения, подобные Gmail, и ваш webmail-модуль (или группа модулей) перестанет работать из-за ошибки, то это не должно заблокировать пользовательский интерфейс или помешать пользователям использовать другие части вашего приложения, к примеру, такие как чат. В то же время, как было сказано раньше, было бы идеально, если бы модули могли работать и за пределами вашей архитектуры. В моей лекции я упоминал динамические зависимости — возможность загружать модули, исходя из определенных действий пользователя. К примеру, ребята из Gmail могли бы изначально держать чат закрытым, не загружая его код при открытии страницы. А в тот момент, когда пользователь решит воспользоваться им — соответствующий модуль будет динамически загружен и выполнен. В идеальном случае хотелось бы выполнить это без каких-то негативных эффектов в вашем приложении. ### 4. Насколько легко вы сможете тестировать отдельные модули? Когда вы работаете над масштабными системами, есть вероятность, что различные части этой системы будут использовать миллионы пользователей. Вполне вероятно, что эти части будут использоваться не только в предусмотренных вами ситуациях. В конечном счете, код может использоваться повторно в огромном количестве различных окружений, и важно, чтобы модули были достаточно протестированы. Тестировать модули необходимо и внутри архитектуры, для которой он был изначально разработан, и снаружи. По моему мнению, это дает наибольшую гарантию того, что модуль не сломается при попадании в другую систему. [1]: http://rmurphey.com/blog/2010/08/27/code-org-take-2-structuring-javascript-applications/ [2]: http://michaux.ca/articles/mvc-architecture-for-javascript-applications [3]: http://stackoverflow.com/questions/5112899/knockout-js-vs-backbone-js-vs [4]: http://msdn.microsoft.com/en-us/scriptjunkie/ff706600 ================================================ FILE: _includes/translation/rus/03_think-long-term.md ================================================ В процессе создания архитектуры большого приложения, очень важно думать о будущем. Не только о том, что будет через месяц или через год, но и о том, что будет после этого. Что может измениться? Конечно, невозможно достаточно точно предсказать как ваше приложение будет развиваться, но, вне всякого сомнения, имеет смысл подумать об этом. Думаю, что найдется хотя бы один специфичный аспект вашего приложения, о котором стоит поразмыслить. Разработчики зачастую слишком сильно связывают манипуляцию с DOM-элементами и остальные части приложения, даже если до этого они не поленились разделить бизнес-логику на модули. Подумайте, почему в долгосрочной перспективе это может быть плохой идеей? Один из слушателей моей лекции предположил, что такая архитектура негибка, и может не работать в будущем. Это действительно так, но есть другая проблема, игнорирование которой окажет еще более негативный эффект. {:class="message"} В будущем, вы можете принять решение о **замене** Dojo, jQuery, Zepto или YUI на что-нибудь совершенно иное. Причиной такого перехода может быть производительность, безопасность или дизайн. Это может стать серьезной проблемой, потому как библиотеки не предусматривают простой замены. Цена замены библиотеки будет высокой, если ваше приложение тесно с ней связано. Если вы используете Dojo (как многие слушатели на моей лекции), вы можете быть уверены, что нет ничего лучше, на что имело бы смысл сейчас перейти. Но можете ли вы быть уверены, что в течении двух-трех лет не появится что-нибудь, что вызовет у вас интерес? Что-нибудь, на что вы можете решить перейти. Такое решение может быть достаточно простым для небольших проектов, но для бóльших приложений гибкая архитектура может принести значительную пользу как в финансовом плане, так и с точки зрения экономии времени благодаря тому, что её можно поддерживать **не** заботясь об используемых библиотеках. Подводя итог, взгляните на вашу архитектуру сейчас. Сможете ли вы сегодня сменить вашу библиотеку на любую другую, не переписывая при этом ваше приложение полностью? Если это не так, то вам стоит продолжить чтение. Я думаю, что в архитектуре, которую мы обсуждаем, вы найдёте кое-что интересное. Некоторые из известных JavaScript-разработчиков раньше уже излагали проблемы, о которых я написал выше. Вот три ключевых цитаты, которыми я бы хотел поделиться с вами. {:class="message"} «Секрет создания больших приложений в том, чтобы никогда не создавать больших приложений. Разбейте ваши приложения на маленькие части, а затем собирайте из этих маленьких тестируемых фрагментов ваше большое приложение» **Джастин Майер, автор «JavaScriptMVC»** {:class="message"} «Секрет в том, чтобы признаться самому себе с самого начала, что вы понятия не имеете о том, как ваше приложение будет развиваться. Когда вы согласитесь с этим, вы начнете проектировать систему основываясь на защите. Вы определите ключевые области, в которых, вероятнее всего будут происходить изменения. Очень часто это не составляет труда, если потратить на это немного времени. К примеру, вы ожидаете, что любая часть приложения, которая взаимодействует с другой системой — это потенциальная мишень для изменений. И вы понимаете, что здесь вам понадобится абстракция». **Николас Закас, автор книги «Высокопроизводительный JavaScript** И последняя, но тоже очень важная цитата: {:class="message"} «Чем сильнее компоненты связаны между собой, тем меньше возможностей для их повторного использования, тем сложнее вносить изменения, не получая при этом различных побочных эффектов в самых неожиданных местах» **Ребекка Мёрфи, автор книги «Фундаментальные основы jQuery»** Эти принципы необходимы для создания архитектуры, способной выдержать испытание временем. Важно всегда помнить о них. ================================================ FILE: _includes/translation/rus/04_brainstorming.md ================================================ Давайте немного подумаем, что мы хотим получить. {:class="message"} Мы хотим получить слабосвязанную архитектуру с функциональностью, разделенную на **независимые модули**, которые, в идеале, не должны иметь зависимостей друг от друга. Когда случается что-то интересное, модули **сообщают** об этом другим частям приложения, а промежуточный слой интерпретирует их сообщения и необходимым образом реагирует на них. Например, у нас есть JavaScript-приложение, отвечающее за онлайн-пекарню. Одно из интересных нам сообщений может быть таким: «Партия из 42 батонов готова к доставке». Мы используем отдельный слой для обработки сообщений модулей, чтобы а) модули не взаимодействовали напрямую с ядром, б) модули не взаимодействовали напрямую друг с другом. Это помогает не допустить падения приложения из-за различных ошибок внутри одного из модулей. Также это позволяет нам перезапускать модули если они вдруг перестали работать из-за какой-нибудь ошибки. Еще один момент — безопасность. На самом деле, немногие из нас заботятся о внутренней безопасности своих приложений в должной мере. Когда мы определяем структуру приложения, мы говорим себе, что мы достаточно умны для того, чтобы понимать что в нашем коде должно быть публичным, а что приватным. Хорошо, но поможет ли это если вы решите определить что именно разрешено модулю выполнять в системе? К примеру, в моем приложении, ограничив доступ из модуля веб-чата к интерфейсу модуля администрирования, я смогу уменьшить шансы на успешное использование XSS уязвимостей, которые я не смог найти в виджете. Модули не должны иметь доступ ко всему. Вероятно, в вашей существующей архитектуре они могут использовать любые части системы, но уверены ли вы, что это действительно необходимо? Промежуточный слой, проверяющий имеет ли модуль доступ к определенной части вашего фреймворка, обеспечивает большую безопасность вашей системы. Фактически это значит что модули могут взаимодействовать только с теми компонентами системы, с которыми мы разрешим им взаимодействовать. ### Архитектура, которую я предлагаю вам Архитектура, о которой мы говорим, представляет из себя комбинацию трех известных шаблонов проектирования: модуль, фасад и медиатор. В отличии от традиционной модели, в которой модули напрямую взаимодействуют друг с другом, в этой слабосвязанной архитектуре модули всего лишь публикуют события (в идеале, не зная о других модулях в системе). Медиатор используется для подписки на сообщения от модулей и для решения, каким должен быть ответ на уведомление. Паттерн фасад используется для ограничения действий разрешенных модулям. В следующих главах я более детально расскажу о каждом из этих шаблонов проектирования. ================================================ FILE: _includes/translation/rus/05_module-theory.md ================================================ Вероятно, в каком-то виде вы уже используете модули в своей существующей архитектуре. Если это не так, то в этой главе я покажу вам, как они устроены. {:class="message"} Модули — это **целая** часть любой хорошей архитектуры приложения. Обычно модули выполняют одну определенную задачу в более крупных системах и могут быть взаимозаменяемы. В некоторых реализациях модули могут иметь свои собственные зависимости, которые будут загружены автоматически, собирая вместе таким образом все компоненты системы. Такой подход считается более масштабируемым, в отличие от ручной загрузки модулей или подстановки тега `script`. Каждое нетривиальное приложение должно создаваться из модульных компонентов. Рассмотрим GMail: вы можете рассматривать модули, как независимые единицы функциональности, которые могут и должны существовать сами по себе; возьмём к примеру чат. Скорее всего он основан на своём отдельном модуле чата, но так как этот модуль скорее всего очень сложный, то он вероятно состоит из более мелких вспомогательных модулей. Например, один из таких модулей мог бы отвечать за использование смайликов и он же мог бы использоваться не только в чате, но также и в почте. {:class="message"} В рассматриваемой архитектуре модули имеют **очень ограниченные знания** о том, что происходит в других частях системы. Вместо этого мы делегируем ответственность медиатору и фасаду. В этом и заключается идея нашей архитектуры — если модуль заботится исключительно о том, чтобы уведомить систему об интересующих ее происшествиях, и не волнуется запущены ли другие модули, то система может добавлять, удалять или заменять одни модули, не ломая при этом другие, что было бы невозможно при сильной связанности. Слабая связанность — необходимое условие для того, чтобы такая идея была возможна. Она делает поддержку модулей проще, удаляя зависимости в коде там, где это возможно. В вашем случае, одни модули должны работать корректно вне зависимости от того в каком порядке загрузились другие модули. Когда слабая связанность реализована эффективно, становится очевидно, как изменения в одной части системы влияют на другие ее части. В JavaScript есть несколько способов реализации модулей, включая шаблон «Модуль» и Object Literal (литеральная запись объекта `var obj = {};`). Опытные разработчики должно быть уже знакомы с ними. Если это так, то вы можете пропустить следующую главу и перейти сразу к главе «Модули CommonJS». ================================================ FILE: _includes/translation/rus/06_the-module-pattern.md ================================================ «Модуль» — это популярная реализация паттерна, инкапсулирующего приватную информацию, состояние и структуру, используя замыкания. Это позволяет оборачивать публичные и приватные методы и переменные в модули, и предотвращать их попадание в глобальный контекст, где они могут конфликтовать с интерфейсами других разработчиков. Паттерн «модуль» возвращает только публичную часть API, оставляя всё остальное доступным только внутри замыканий. Это хорошее решение для того, чтобы скрыть внутреннюю логику от посторонних глаз и производить всю тяжелую работу исключительно через интерфейс, который вы определите для использования в других частях вашего приложения. Этот паттерн очень похож на немедленно-вызываемые функции ([IIFE][3]), за тем исключением, что модуль вместо функции, возвращает объект. Важно заметить, что в JavaScript нет настоящей приватности. В отличии от некоторых традиционных языков, он не имеет модификаторов доступа. Переменные технически не могут быть объявлены как публичные или приватные, и нам приходится использовать область видимости для того, чтобы эмулировать эту концепцию. Благодаря замыканию, объявленные внутри модуля переменные и методы доступны только изнутри этого модуля. Переменные и методы, объявленные внутри объекта, возвращаемого модулем, будут доступны всем. Ниже вы можете увидеть корзину покупок, реализованную с помощью паттерна «модуль». Получившийся компонент находится в глобальном объекте `basketModule`, и содержит всё, что ему необходимо. Находящийся внутри него, массив `basket` приватный, и другие части вашего приложения не могут напрямую взаимодействовать с ним. Массив `basket` существует внутри замыкания, созданного модулем, и взаимодействовать с ним могут только методы, находящиеся в том же контексте (например, `addItem()`, `getItem()`). {% highlight javascript %} var basketModule = (function() { var basket = []; // приватная переменная return { // методы доступные извне addItem: function(values) { basket.push(values); }, getItemCount: function() { return basket.length; }, getTotal: function() { var q = this.getItemCount(),p=0; while(q--){ p+= basket[q].price; } return p; } } }()); {% endhighlight %} Внутри модуля, как вы заметили, мы возвращаем объект. Этот объект автоматически присваивается переменной `basketModule`, так что с ним можно взаимодействовать следующим образом: {% highlight javascript %} // basketModule - это объект со свойствами, которые могут также быть и методами: basketModule.addItem({item:'bread', price:0.5}); basketModule.addItem({item:'butter', price:0.3}); console.log(basketModule.getItemCount()); console.log(basketModule.getTotal()); // А следующий ниже код работать не будет: console.log(basketModule.basket); // undefined потому что не входит в возвращаемый объект console.log(basket); // массив доступен только из замыкания {% endhighlight %} Методы выше фактически помещены в неймспейс `basketModule`. Исторически, паттерн «модуль» был разработан в 2003 году группой людей, в число которых входил [Ричард Корнфорд][4]. Позднее этот паттерн был популяризован Дугласом Крокфордом в его лекциях, и открыт заново в блоге YUI благодаря Эрику Мирагилиа. Давайте посмотрим на реализацию «модуля» в различных библиотеках и фреймворках. **Dojo** Dojo старается обеспечивать поведение похожее на классы с помощью `dojo.declare`, который, кроме создания «модулей», также используется и для других вещей. Давайте попробуем, для примера, определить `basket` как модуль внутри неймспейса `store`: {% highlight javascript %} // традиционный способ var store = window.store || {}; store.basket = store.basket || {}; // с помощью dojo.setObject dojo.setObject("store.basket.object", (function() { var basket = []; function privateMethod() { console.log(basket); } return { publicMethod: function() { privateMethod(); } }; }())); {% endhighlight %} Лучшего результата можно добиться, используя `dojo.provide` и миксины. **YUI** Следующий код, по большей части, основан на примере реализации паттерна «модуль» в фреймворке YUI, разработанным Эриком Миргалиа, но более самодокументирован. {% highlight javascript %} YAHOO.store.basket = function () { // приватная переменная: var myPrivateVar = "Ко мне можно получить доступ только из YAHOO.store.basket."; // приватный метод: var myPrivateMethod = function() { YAHOO.log("Я доступен только при вызове из YAHOO.store.basket"); } return { myPublicProperty: "Я - публичное свойство", myPublicMethod: function() { YAHOO.log("Я - публичный метод"); // Будучи внутри корзины я могу получить доступ к приватным переменный и методам: YAHOO.log(myPrivateVar); YAHOO.log(myPrivateMethod()); // Родной контекст метода myPublicMethod сохранён // поэтому мы имеет доступ к this YAHOO.log(this.myPublicProperty); } }; }(); {% endhighlight %} **jQuery** Существует множество способов, чтобы представить jQuery-код в виде паттерна «модуль», даже если этот код не напоминает привычные jQuery-плагины. Бен Черри ранее предлагал способ, при котором, если у модулей есть общие черты, то они объявляются через функцию-обертку. В следующем примере функция `library` используется для объявления новой библиотеки и автоматически при создании библиотеки (т.е. модуля), связывает вызов метода `init` с `document.ready`. {% highlight javascript %} function library(module) { $(function() { if (module.init) { module.init(); } }); return module; } var myLibrary = library(function() { return { init: function() { /* код модуля */ } }; }()); {% endhighlight %} {:class="message"} **Ссылки по теме:** [Бен Черри — Погружение в паттерн «Модуль»][5] [Джон Ханн — Будущее — это модули, а не фреймворки][6] [Натан Смит — Ссылки на window и document в модулях (gist)][7] [3]: http://benalman.com/news/2010/11/immediately-invoked-function-expression/ [4]: http://groups.google.com/group/comp.lang.javascript/msg/9f58bd11bd67d937 [5]: http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth [6]: http://lanyrd.com/2011/jsconf/sfgdk/ [7]: https://gist.github.com/274388 ================================================ FILE: _includes/translation/rus/07_object-literal-notation.md ================================================ В литеральной нотации объект описывается внутри блока фигурных скобок (`{}`), как набор разделенных запятой пар ключ/значение. Ключи объекта могут быть как строками, так и идентификаторами. После имени ставится двоеточие. В объекте не должно стоять запятой после последней пары ключ/значение, так как это может привести к ошибкам. Литерал объекта не требует использования оператора `new` для создания экземпляра, но он не должен стоять в начале выражения, так как открытая `{` может быть воспринята как начало блока. Ниже вы можете увидеть пример модуля, определенного с помощью литеральной нотации объекта. Новые члены объекта могут быть добавлены с помощью конструкции `myModule.property = 'someValue';` {:.message} Паттерн «модуль» может быть полезен для многих вещей. Но если вы считаете, что вам не нужно делать приватными некоторые методы или свойства, то литерал объекта — более чем подходящий выбор. {% highlight javascript %} var myModule = { myProperty: 'someValue', // Литералы объектов могут содержать свойства и методы. // ниже в свойстве определен другой объект, // для описания конфигурации: myConfig: { useCaching: true, language: 'en' }, // Очень простой метод myMethod: function() { console.log('I can haz functionality?'); }, // вывод значения заданного в конфигурации myMethod2: function() { console.log('Caching is: ' + ((this.myConfig.useCaching) ? 'enabled' : 'disabled')); }, // переопределение конфигурации myMethod3: function(newConfig) { if (typeof newConfig == 'object') { this.myConfig = newConfig; console.log(this.myConfig.language); } } }; myModule.myMethod(); // 'I can haz functionality' myModule.myMethod2(); // Вывод 'enabled' myModule.myMethod3({language:'fr',useCaching:false}); // 'fr' {% endhighlight %} {:class="message"} **Ссылки по теме:** [Ребекка Мёрфи — Использование объектов для организации вашего кода][1] [Стоян Стефанов — 3 способа определения класса в JavaScript ][2] [Бен Алман — Разъяснения по литералам объектов (понятия JSON-объект не существует)][3] [Джон Резиг - Простое наследование в JavaScript][4] [1]: http://blog.rebeccamurphey.com/2009/10/15/using-objects-to-organize-your-code [2]: http://www.phpied.com/3-ways-to-define-a-javascript-class/ [3]: http://benalman.com/news/2010/03/theres-no-such-thing-as-a-json/ [4]: http://ejohn.org/blog/simple-javascript-inheritance/ ================================================ FILE: _includes/translation/rus/08_commonjs-modules.md ================================================ Возможно, вы что-то слышали о [CommonJS][4] за последние пару лет. CommonJS — это добровольная рабочая группа, которая проектирует, прототипирует и стандартизирует различные JavaScript API. На сегодняшний день они ратифицировали стандарты для модулей и пакетов — CommonJS определяют простой API для написания модулей, которые могут быть использованы в браузере с помощью тега ` {% include analytics.html %} ================================================ FILE: _posts/04-02-2014-the-mediator-pattern.md ================================================ --- layout: page title: "Глава 10. Паттерн «Медиатор»" path: translation/rus/10_the-mediator-pattern.md published: true --- {% include translation/rus/10_the-mediator-pattern.md %} ================================================ FILE: _posts/07-02-2014-applying-the-facade-abstraction-of-the-core.md ================================================ --- layout: page title: "Глава 11. Использование фасада: абстракция ядра" path: translation/rus/11_applying-the-facade-abstraction-of-the-core.md published: true --- {% include translation/rus/11_applying-the-facade-abstraction-of-the-core.md %} ================================================ FILE: _posts/09-01-2014-intro.md ================================================ --- layout: page title: "Введение" path: translation/rus/00_intro.md published: true --- {% include translation/rus/00_intro.md %} ================================================ FILE: _posts/10-01-2014-what-exactly-is-a-large-javascript-application.md ================================================ --- layout: page title: "Глава 1. Что представляет собой «большое» JavaScript-приложение?" path: translation/rus/01_what-exactly-is-a-large-javascript-application.md published: true --- {% include translation/rus/01_what-exactly-is-a-large-javascript-application.md %} ================================================ FILE: _posts/11-01-2014-lets-review-your-current-architecture.md ================================================ --- layout: page title: "Глава 2. Давайте обсудим вашу существующую архитектуру" path: translation/rus/02_lets-review-your-current-architecture.md published: true --- {% include translation/rus/02_lets-review-your-current-architecture.md %} ================================================ FILE: _posts/11-02-2014-applying-the-mediator-the-application-core.md ================================================ --- layout: page title: "Глава 12. Использование медиатора: ядро приложения" path: translation/rus/12_applying-the-mediator-the-application-core.md published: true --- {% include translation/rus/12_applying-the-mediator-the-application-core.md %} ================================================ FILE: _posts/14-02-2014-tying-it-all-together.md ================================================ --- layout: page title: "Глава 13. Собираем все вместе" path: translation/rus/13_tying-it-all-together.md published: true --- {% include translation/rus/13_tying-it-all-together.md %} ================================================ FILE: _posts/15-01-2014-think-long-term.md ================================================ --- layout: page title: "Глава 3. Думай о будущем" path: translation/rus/03_think-long-term.md published: true --- {% include translation/rus/03_think-long-term.md %} ================================================ FILE: _posts/16-01-2014-brainstorm.md ================================================ --- layout: page title: "Глава 4. Мозговой штурм" path: translation/rus/04_brainstorming.md published: true --- {% include translation/rus/04_brainstorming.md %} ================================================ FILE: _posts/17-01-2014-module-theory.md ================================================ --- layout: page title: "Глава 5. Теория модулей" path: translation/rus/05_module-theory.md published: true --- {% include translation/rus/05_module-theory.md %} ================================================ FILE: _posts/18-02-2014-beyond-pub-sub-automatic-event-registration.md ================================================ --- layout: page title: "Глава 14. Развитие идеи медиатора: автоматическая регистрация событий" path: translation/rus/14_beyond-pub-sub-automatic-event-registration.md published: true --- {% include translation/rus/14_beyond-pub-sub-automatic-event-registration.md %} ================================================ FILE: _posts/19-02-2014-faq.md ================================================ --- layout: page title: "Часто задаваемые вопросы" path: translation/rus/15_frequently-asked-questions.md published: true --- {% include translation/rus/15_frequently-asked-questions.md %} ================================================ FILE: _posts/20-02-2014-credits.md ================================================ --- layout: page title: "Благодарности" path: translation/rus/16_credits.md published: true --- {% include translation/rus/16_credits.md %} ================================================ FILE: _posts/21-01-2014-module-pattern.md ================================================ --- layout: page title: "Глава 6. Паттерн «Модуль»" path: translation/rus/06_the-module-pattern.md published: true --- {% include translation/rus/06_the-module-pattern.md %} ================================================ FILE: _posts/24-01-2014-object-literal-notation.md ================================================ --- layout: page title: "Глава 7. Литеральная нотация объектов" path: translation/rus/07_object-literal-notation.md published: true --- {% include translation/rus/07_object-literal-notation.md %} ================================================ FILE: _posts/28-01-2014-commonjs-modules.md ================================================ --- layout: page title: "Глава 8. Модули CommonJS" path: translation/rus/08_commonjs-modules.md published: true --- {% include translation/rus/08_commonjs-modules.md %} ================================================ FILE: _posts/31-01-2014-the-facade-pattern.md ================================================ --- layout: page title: "Глава 9. Паттерн «Фасад»" path: translation/rus/09_the-facade-pattern.md published: true --- {% include translation/rus/09_the-facade-pattern.md %} ================================================ FILE: _stylus/_nib/Readme.md ================================================ [![Build Status](https://travis-ci.org/visionmedia/nib.png?branch=master)](https://travis-ci.org/visionmedia/nib) # Nib Stylus mixins, utilities, components, and gradient image generation. Don't forget to check out the [documentation](http://visionmedia.github.com/nib/). ## Installation ```bash $ npm install nib ``` If the image generation features of Nib are desired, such as generating the linear gradient images, install [node-canvas](http://github.com/learnboost/node-canvas): ```bash $ npm install canvas ``` ## JavaScript API Below is an example of how to utilize nib and stylus with the connect framework (or express). ```javascript var connect = require('connect') , stylus = require('stylus') , nib = require('nib'); var server = connect(); function compile(str, path) { return stylus(str) .set('filename', path) .set('compress', true) .use(nib()); } server.use(stylus.middleware({ src: __dirname , compile: compile })); ``` ## Stylus API To gain access to everything nib has to offer, simply add: ```css @import 'nib' ``` Or you may also pick and choose based on the directory structure in `./lib`, for example: ```css @import 'nib/gradients' @import 'nib/overflow' ``` to be continued.... ## More Information - Introduction [screencast](http://www.screenr.com/M6a) ## Testing You will first need to install the dependencies: ```bash $ npm install -d ``` Run the automated test cases: ```bash $ npm test ``` For visual testing run the test server: ```bash $ npm run-script test-server ``` Then visit `localhost:3000` in your browser. ## Contributors I would love more contributors. And if you have helped out, you are awesome! I want to give a huge thanks to these people: - [TJ Holowaychuk](https://github.com/visionmedia) (Original Creator) - [Sean Lang](https://github.com/slang800) (Current Maintainer) - [Isaac Johnston](https://github.com/superstructor) - [Everyone Else](https://github.com/visionmedia/nib/contributors) ## License (The MIT License) Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: _stylus/_nib/iconic/demo.html ================================================ ================================================ FILE: _stylus/_nib/iconic/iconic.css ================================================ @font-face { font-family: 'IconicStroke'; src: url("iconic_stroke.eot"); src: local('IconicStroke'), url("iconic_stroke.svg#iconic") format('svg'), url("iconic_stroke.otf") format('opentype'); } .iconic { color:inherit; font-family: "IconicStroke"; } a.iconic:hover { color:inherit; } .iconic.home:before { content: '!'; } .iconic.at:before { content: "@"; } .iconic.quote:before { content: '"'; } .iconic.quote-alt:before { content: "'"; } .iconic.arrow-up:before { content: "3"; } .iconic.arrow-right:before { content: "4"; } .iconic.arrow-bottom:before { content: "5"; } .iconic.arrow-left:before { content: "6"; } .iconic.arrow-up-alt:before { content: "#"; } .iconic.arrow-right-alt:before { content: "$"; } .iconic.arrow-bottom-alt:before { content: "%"; } .iconic.arrow-left-alt:before { content: "^"; } .iconic.move:before { content: "9"; } .iconic.move-vertical:before { content: "8"; } .iconic.move-horizontal:before { content: "7"; } .iconic.move-alt:before { content: "("; } .iconic.move-vertical-alt:before { content: "*"; } .iconic.move-horizontal-alt:before { content: "&"; } .iconic.cursor:before { content: ")"; } .iconic.plus:before { content: "+"; } .iconic.plus-alt:before { content: "="; } .iconic.minus:before { content: "-"; } .iconic.minus-alt:before { content: "_"; } .iconic.new-window:before { content: "1"; } .iconic.dial:before { content: "2"; } .iconic.lightbulb:before { content: "0"; } .iconic.link:before { content: "/"; } .iconic.image:before { content: "?"; } .iconic.article:before { content: ">"; } .iconic.read-more:before { content: "."; } .iconic.headphones:before { content: ","; } .iconic.equalizer:before { content: "<"; } .iconic.fullscreen:before { content: ":"; } .iconic.exit-fullscreen:before { content: ";"; } .iconic.spin:before { content: "["; } .iconic.spin-alt:before { content: "{"; } .iconic.moon:before { content: "]"; } .iconic.sun:before { content: "}"; } .iconic.map-pin:before { content: "\\"; } .iconic.pin:before { content: "|"; } .iconic.eyedropper:before { content: "~"; } .iconic.denied:before { content: "`"; } .iconic.calendar:before { content: "a"; } .iconic.calendar-alt:before { content: "A"; } .iconic.bolt:before { content: "b"; } .iconic.clock:before { content: "c"; } .iconic.document:before { content: "d"; } .iconic.book:before { content: "e"; } .iconic.book-alt:before { content: "E"; } .iconic.magnifying-glass:before { content: "f"; } .iconic.tag:before { content: "g"; } .iconic.heart:before { content: "h"; } .iconic.info:before { content: "i"; } .iconic.chat:before { content: "j"; } .iconic.chat-alt:before { content: "J"; } .iconic.key:before { content: "k"; } .iconic.unlocked:before { content: "l"; } .iconic.locked:before { content: "L"; } .iconic.mail:before { content: "m"; } .iconic.mail-alt:before { content: "M"; } .iconic.phone:before { content: "n"; } .iconic.box:before { content: "o"; } .iconic.pencil:before { content: "p"; } .iconic.pencil-alt:before { content: "P"; } .iconic.comment:before { content: "q"; } .iconic.comment-alt:before { content: "Q"; } .iconic.rss:before { content: "r"; } .iconic.star:before { content: "s"; } .iconic.trash:before { content: "t"; } .iconic.user:before { content: "u"; } .iconic.volume:before { content: "v"; } .iconic.mute:before { content: "V"; } .iconic.cog:before { content: "w"; } .iconic.cog-alt:before { content: "W"; } .iconic.x:before { content: "x"; } .iconic.x-alt:before { content: "X"; } .iconic.check:before { content: "y"; } .iconic.check-alt:before { content: "Y"; } .iconic.beaker:before { content: "z"; } .iconic.beaker-alt:before { content: "Z"; } ================================================ FILE: _stylus/_nib/index.styl ================================================ @import 'lib/nib/' ================================================ FILE: _stylus/_nib/lib/nib/border.styl ================================================ /* * border: * border: ... */ border(color, args...) if color is a 'color' border: 1px solid color args else border: arguments ================================================ FILE: _stylus/_nib/lib/nib/clearfix.styl ================================================ /* * The Magnificent Micro Clearfix * * Useful for clearing floats without structural markup. * Prevents margin-collapsing on child elements in most cases. * * Known issues: * * 1. For IE 6/7 when applied to an element that contains only left-floated * children the bottom margin on child elements will be collapsed. * * 2. For Firefox versions prior to 3.5 when applied to the first child element * of body, and the element does not have non-zero padding, extra space will * appear between the body and the first child. * * See http://nicolasgallagher.com/micro-clearfix-hack/ * and http://j.mp/bestclearfix * */ clearfix() &:before &:after content: "" display: table &:after clear: both zoom: 1 if support-for-ie ================================================ FILE: _stylus/_nib/lib/nib/color-image.styl ================================================ color-image(color) error('node-canvas is required for color-image()') unless has-canvas colorImage = create-color-image(color) 'url(%s)' % color-data-uri(colorImage) ================================================ FILE: _stylus/_nib/lib/nib/config.styl ================================================ /* * Support for ie defaulting to true. */ support-for-ie ?= true /* * Default vendor prefixes. */ vendor-prefixes ?= webkit moz o ms official ================================================ FILE: _stylus/_nib/lib/nib/flex.styl ================================================ /* * Vendor "display: flex" support with fallback to obsolete versions. */ flex-version ?= box flex // // 1. Display values // - http://www.w3.org/TR/css3-flexbox/#flex-containers // display(type, args...) if flex == type || inline-flex == type if box in flex-version if flex == type display: -ms-flexbox args display: vendor-value(box args, only: moz webkit) else display: -ms-inline-flexbox args display: vendor-value(inline-box args, only: moz webkit) if flex in flex-version display: vendor-value(arguments, only: webkit official) // overwrites old webkit else display: arguments /* * New syntax for browsers like Google Chrome. * Plus a translation to the old syntax, if possible. */ // // 5. Ordering and Orientation // - http://www.w3.org/TR/css3-flexbox/#ordering-and-orientation // -flex-obsolete-direction(direction) if box in flex-version if row-reverse == direction || column-reverse == direction vendor('box-direction', reverse, ignore: ms official) if row == direction || row-reverse == direction vendor('box-orient', horizontal, ignore: ms official) else if column == direction || column-reverse == direction vendor('box-orient', vertical, ignore: ms official) -flex-obsolete-wrap(value) if box in flex-version // WARN: wrap-reverse does not have a box equivalent. This will render in different manners // on box vs. flex values. if 'wrap' == value || wrap-reverse == value vendor('box-lines', multiple, ignore: ms official) else if nowrap == value vendor('box-lines', single, ignore: ms official) flex-direction(direction) // obsolete -flex-obsolete-direction(direction) // new if flex in flex-version vendor('flex-direction', arguments, only: webkit ms official) flex-wrap(value) // obsolete -flex-obsolete-wrap(value) if flex in flex-version vendor('flex-wrap', arguments, only: webkit ms official) flex-flow() // obsolete -flex-obsolete-direction(arguments[0]) -flex-obsolete-direction(arguments[1]) -flex-obsolete-wrap(arguments[0]) -flex-obsolete-wrap(arguments[1]) // new if flex in flex-version vendor('flex-flow', arguments, only: webkit ms official) order() // obsolete if box in flex-version vendor('box-ordinal-group', arguments, ignore: ms official) // new if flex in flex-version vendor('flex-order', arguments, only: ms) vendor('order', arguments, only: webkit official) // // 7. Flexibility // - http://www.w3.org/TR/css3-flexbox/#flexibility // flex-grow(growth) // obsolete if box in flex-version vendor('box-flex', growth) // new if flex in flex-version vendor('flex-grow', arguments, only: webkit official) flex-basis() if flex in flex-version vendor('flex-basis', arguments, only: webkit official) flex-shrink() if flex in flex-version vendor('flex-shrink', arguments, only: webkit official) flex(growth) // obsolete if box in flex-version shrink = 1 if none == growth || initial == growth // Well known values shrink = 0 if none == growth growth = 0 else if is-width(growth) == true // Basis is defined as the first parameter growth = arguments[1] || 0 shrink = arguments[2] if 3 <= length(arguments) else if arguments[1] && is-width(arguments[1]) == false // Growth is first and shrink is second shrink = arguments[1] // Since we can't make the distinction between growing and shrinking in the box model, take // the one that provides the most flexibility. vendor('box-flex', max(growth, shrink), ignore: ms) // new if flex in flex-version vendor('flex', arguments, only: webkit ms official) // converts the justification alignment -convert-justify(align) if flex-start == align return start else if flex-end == align return end else if space-around == align return distribute else if space-between == align return justify else return align // // 8. Alignment // - http://www.w3.org/TR/css3-flexbox/#alignment // justify-content(align) // obsolete if box in flex-version vendor('box-pack', -convert-justify(align), ignore: ms official) // new if flex in flex-version vendor('flex-pack', -convert-justify(align), only: ms) vendor('justify-content', align, only: webkit official) align-content(align) // WARN: Obsolete spec does not allow for adjustment here if flex in flex-version vendor('flex-line-pack', -convert-justify(align), only: ms) vendor('align-content', align, only: webkit official) // converts alignment from 'flex' to normal value -convert-alignment(align) if flex-start == align return start else if flex-end == align return end else return align align-items(align) // obsolete if box in flex-version vendor('box-align', -convert-alignment(align), ignore: ms official) // new if flex in flex-version vendor('flex-align', -convert-alignment(align), only: ms) vendor('align-items', arguments, only: webkit official) align-self(align) // WARN: Obsolete spec does not allow for overriding alignment on individual items. if flex in flex-version vendor('align-self', align, only: webkit official) vendor('flex-item-align', -convert-alignment(align), only: ms) ================================================ FILE: _stylus/_nib/lib/nib/gradients.styl ================================================ @import 'config' /* * Implicit color stop position. */ pos-in-stops(i, stops) len = length(stops) if len - 1 == i 100% else if i unit(i / len * 100, '%') else 0 /* * Normalize color stops: * * - (color pos) -> (pos color) * - (color) -> (implied-pos color) * */ normalize-stops(stops) stops = clone(stops) for stop, i in stops if length(stop) == 1 color = stop[0] stop[0] = pos-in-stops(i, stops) stop[1] = color else if typeof(stop[1]) == 'unit' pos = stop[1] stop[1] = stop[0] stop[0] = pos stops /* * Join color stops with the given translation function. */ join-stops(stops, translate) str = '' len = length(stops) for stop, i in stops str += ', ' if i pos = stop[0] color = stop[1] str += translate(color, pos) unquote(str) /* * Standard color stop. */ std-stop(color, pos) '%s %s' % (color pos) /* * Create a linear gradient with the given start position * and variable number of color stops. * * Examples: * * background: linear-gradient(top, red, green, blue) * background: linear-gradient(bottom, red, green 50%, blue) * background: linear-gradient(bottom, red, 50% green, blue) * background: linear-gradient(bottom, red, 50% green, 90% white, blue) * */ linear-gradient(start, stops...) error('color stops required') unless length(stops) unquote('linear-gradient(' + join(', ',arguments) + ')') /* * Create a linear gradient image with the given start position * and variable number of color stops. */ linear-gradient-image(start, stops...) error('node-canvas is required for linear-gradient-image()') unless has-canvas stops = stops[0] if length(stops) == 1 error('gradient image size required') unless start[0] is a 'unit' size = start[0] start = start[1] or 'top' grad = create-gradient-image(size, start) stops = normalize-stops(stops) add-color-stop(grad, stop[0], stop[1]) for stop in stops 'url(%s)' % gradient-data-uri(grad) ================================================ FILE: _stylus/_nib/lib/nib/iconic.styl ================================================ iconic-stroke(path) @font-face font-family: 'IconicStroke' src: url(path + '/iconic_stroke.eot') src: local('☺'), url(path + '/iconic_stroke.ttf') format('truetype'), url(path + '/iconic_stroke.svg#iconic') format('svg') font-weight: normal font-style: normal ================================================ FILE: _stylus/_nib/lib/nib/image.styl ================================================ /* * Define background-image as `path` with optional width and height, adding an * @2x variant. * * affected by github.com/LearnBoost/stylus/issues/1050 and * github.com/LearnBoost/stylus/issues/1038 ... refactor when those are closed */ image(path, w = auto, h = auto, min_pixel_ratio = 1.5) background-image: url(path) s = 'all and (-webkit-min-device-pixel-ratio:' + min_pixel_ratio + '),' s = s + '(min--moz-device-pixel-ratio:' + min_pixel_ratio + '),' s = s + '(-o-min-device-pixel-ratio:' + min_pixel_ratio + '/1),' s = s + '(min-device-pixel-ratio:' + min_pixel_ratio + '),' s = s + '(min-resolution:' + unit(min_pixel_ratio*92, dpi) + '),' s = s + '(min-resolution:' + unit(min_pixel_ratio, dppx) + ')' @media s ext = extname(path) path = pathjoin(dirname(path), basename(path, ext) + '@2x' + ext) background-image: url(path) if w in (cover contain) and h == auto h = null background-size: w h ================================================ FILE: _stylus/_nib/lib/nib/index.styl ================================================ @import 'border' @import 'clearfix' @import 'color-image' @import 'flex' @import 'gradients' @import 'iconic' @import 'image' @import 'overflow' @import 'positions' @import 'reset' @import 'text' @import 'vendor' @import 'size' ================================================ FILE: _stylus/_nib/lib/nib/overflow.styl ================================================ /* * Overflow utility. Maps to regular overflow, and adds an ellipsis value. * * Synopsis: * * overflow: * * Examples: * * overflow: auto * overflow: hidden * overflow: ellipsis * */ overflow() if arguments[0] == ellipsis ellipsis() else overflow: arguments ================================================ FILE: _stylus/_nib/lib/nib/positions.styl ================================================ // helper -pos(type, args) i = 0 position: unquote(type) for j in (1..4) if length(args) > i {args[i]}: args[i + 1] is a 'unit' ? args[i += 1] : 0 i += 1 /* * Position utility. * * Synopsis: * * fixed: [n] [n] * * Examples: * * fixed: top left * fixed: top 5px left * fixed: top left 5px * fixed: top 5px left 5px * */ fixed() -pos('fixed', arguments) /* * Position utility. * * Synopsis: * * absolute: [n] [n] * * Examples: * * absolute: top left * absolute: top 5px left * absolute: top left 5px * absolute: top 5px left 5px * */ absolute() -pos('absolute', arguments) /* * Position utility. * * Synopsis: * * relative: [n] [n] * * Examples: * * relative: top left * relative: top 5px left * relative: top left 5px * relative: top 5px left 5px * */ relative() -pos('relative', arguments) ================================================ FILE: _stylus/_nib/lib/nib/reset.styl ================================================ // Based on [Eric Meyer's reset](http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/) global-reset() html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td reset-box-model() reset-font() body reset-body() ol, ul list-style: none table reset-table() caption, th, td reset-table-cell() a img border: none nested-reset() div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, pre, a, abbr, acronym, address, code, del, dfn, em, img, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, caption, tbody, tfoot, thead, tr reset-box-model() reset-font() table reset-table() caption, th, td reset-table-cell() a img border: none reset-box-model() margin: 0 padding: 0 border: 0 outline: 0 reset-font() font-weight: inherit font-style: inherit font-family: inherit font-size: 100% vertical-align: baseline reset-body() line-height: 1 color: black background: white reset-table() border-collapse: separate border-spacing: 0 vertical-align: middle reset-table-cell() text-align: left font-weight: normal vertical-align: middle reset-html5() article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary, main reset-box-model() display: block audio, canvas, video display inline-block *display inline *zoom 1 audio:not([controls]),[hidden] display none ================================================ FILE: _stylus/_nib/lib/nib/size.styl ================================================ /* * Size utility. * * Synopsis: * * size: | * * Examples: * * size: 100% 30px * yields: * width: 100% * height: 30px * * size: 5px * yields: * width: 5px * height: 5px * */ size() if length(arguments) == 1 width: arguments[0] height: arguments[0] else width: arguments[0] height: arguments[1] ================================================ FILE: _stylus/_nib/lib/nib/text/aliases.styl ================================================ /* * Alias of "nowrap". */ no-wrap = unquote('nowrap') /* * Alias of "white-space". */ whitespace() white-space: arguments ================================================ FILE: _stylus/_nib/lib/nib/text/ellipsis.styl ================================================ /* * Ellipsis with wrapping disabled by default. */ ellipsis(no-wrap = true) if no-wrap white-space: nowrap overflow: hidden text-overflow: ellipsis ================================================ FILE: _stylus/_nib/lib/nib/text/hide-text.styl ================================================ /* * Hide text. */ hide-text() text-indent: 101% white-space: nowrap overflow: hidden ================================================ FILE: _stylus/_nib/lib/nib/text/index.styl ================================================ @import './aliases' @import './ellipsis' @import './hide-text' @import './replace-text' ================================================ FILE: _stylus/_nib/lib/nib/text/replace-text.styl ================================================ /* * Replace text with an image. */ replace-text(image, x=50%, y=50%) hide-text() background-image image background-repeat no-repeat background-position x y ================================================ FILE: _stylus/_nib/lib/nib/vendor.styl ================================================ use('../nodes/vendor-helpers.js') @import 'config' /* * Alias "nowrap" as "no-wrap". */ no-wrap = unquote('nowrap') /* * Helper to find out if a given value is a width */ is-width(val) if auto == val return true else if val && 'unit' == type(val) // Stylus does not short circuit so we need to perform this as a distinct // operation to prevent errors return '' != unit(val) return false /* * Vendor support for the given prop / arguments, optionally specifying the * only prefixes to utilize, or those which should be ignored. */ vendor(prop, args, only = null, ignore = null, vendor-property = true) need_normalize = !vendor-property or prop in ('transition' 'transition-property' 'border-image' 'border-image-slice') for prefix in vendor-prefixes unless (only and !(prefix in only)) or (ignore and prefix in ignore) if official == prefix if need_normalize {prop}: normalize(prop,('%s' % args)) else {prop}: args else newprop = prop newprop = '-' + prefix + '-' + prop if vendor-property if need_normalize {newprop}: normalize(prop,('%s' % args),prefix) else {newprop}: args /* * Vendorize the given value. */ vendor-value(arg, only = null, ignore = null) prop = current-property[0] for prefix in vendor-prefixes unless (only and !(prefix in only)) or (ignore and prefix in ignore) or official == prefix add-property(prop, '-%s-%s' % (prefix arg)) arg /* * Vendor "box-shadow" support. */ box-shadow() vendor('box-shadow', arguments, only: webkit official) /* * Vendor "user-select" support. */ user-select() vendor('user-select', arguments, only: webkit moz ms official) /* * Vendor "column-count" support. */ column-count() vendor('column-count', arguments, only: webkit moz official) /* * Vendor "column-gap" support. */ column-gap() vendor('column-gap', arguments, only: webkit moz official) /* * Vendor "column-rule" support. */ column-rule() vendor('column-rule', arguments, only: webkit moz official) /* * Vendor "column-rule-color" support. */ column-rule-color() vendor('column-rule-color', arguments, only: webkit moz official) /* * Vendor "column-rule-width" support. */ column-rule-width() vendor('column-rule-width', arguments, only: webkit moz official) /* * Vendor "column-rule-style" support. */ column-rule-style() vendor('column-rule-style', arguments, only: webkit moz official) /* * Vendor "column-width" support. */ column-width() vendor('column-width', arguments, only: webkit moz official) /* * Vendor "column-span" support. */ column-span() vendor('column-span', arguments, only: webkit official) /* * Vendor "column-fill" support. */ column-fill() vendor('column-fill', arguments, only: moz) /* * Legacy syntax support for background-clip and background-origin */ legacy-bg-values(property, args) legacy_args = () importance = unquote('') for subargs in args for arg in subargs if arg in (border-box padding-box content-box) arg = unquote('border') if arg == border-box arg = unquote('padding') if arg == padding-box arg = unquote('content') if arg == content-box if arg != '!important' push(legacy_args,arg) else importance = !important vendor(property, unquote(join(', ',legacy_args)) importance, only: moz webkit) /* * Vendor "background-clip" support. */ background-clip() if arguments[0] == text vendor('background-clip', arguments, only: webkit) else legacy-bg-values('background-clip', arguments) background-clip: arguments /* * Vendor "background-origin" support. */ background-origin() legacy-bg-values('background-origin', arguments) background-origin: arguments /* * Vendor "background-size" support. */ background-size() vendor('background-size', arguments, only: webkit moz official) /* * Vendor "transform" support. */ transform() vendor('transform', arguments) /* * Vendor "transform-origin" support. */ transform-origin() vendor('transform-origin', arguments) /* * Vendor "transform-style" support. */ transform-style() vendor('transform-style', arguments) /* * Vendor "border-image" support. */ border-image() vendor('border-image', arguments, only: webkit moz o official) /* * Vendor "transition" support. */ transition() vendor('transition', arguments) /* * Vendor "transition-property" support. */ transition-property() vendor('transition-property', arguments) /* * Vendor "transition-duration" support. */ transition-duration() vendor('transition-duration', arguments) /* * Vendor "transition-timing-function" support. */ transition-timing-function() vendor('transition-timing-function', arguments) /* * Vendor "transition-delay" support. */ transition-delay() vendor('transition-delay', arguments) /* * Vendor "backface-visibility" support. */ backface-visibility() vendor('backface-visibility', arguments, only: webkit moz ms official) /* * Vendor "perspective" support. */ perspective() if mixin vendor('perspective', arguments, only: webkit moz ms official) else 'perspective(%s)' % arguments /* * Vendor "perspective-origin" support. */ perspective-origin() vendor('perspective-origin', arguments, only: webkit moz ms official) /* * Opacity with conditional IE support. */ opacity(n, args...) opacity: n args if support-for-ie val = round(n * 100) if val == 100 -ms-filter: none filter: none else -ms-filter: '"progid:DXImageTransform.Microsoft.Alpha(Opacity=%s)"' % val args filter: 'alpha(opacity=%s)' % val args /* * Vendor "text-size-adjust" */ text-size-adjust() vendor('text-size-adjust', arguments) /* * Alias the "white-space" property. */ whitespace() white-space: arguments /* * Vendor "box-sizing" support. */ box-sizing() vendor('box-sizing', arguments, only: webkit moz official) /* * Vendor "box-orient" support. */ box-orient() vendor('box-orient', arguments, only: webkit moz official) /* * Vendor "box-flex-group" support. */ box-flex-group() vendor('box-flex-group', arguments, only: webkit moz official) /* * Vendor "box-ordinal-group" support. */ box-ordinal-group() vendor('box-ordinal-group', arguments, only: webkit moz ms official) /* * Vendor "box-align" support. */ box-align() vendor('box-align', arguments, only: webkit moz ms official) /* * Vendor "box-pack" support. */ box-pack() vendor('box-pack', arguments, only: webkit moz ms official) /* * Vendor "box-direction" support. */ box-direction() vendor('box-direction', arguments, only: webkit moz ms official) /* * Vendor "animation" support. */ animation() vendor('animation', arguments) /* * Vendor "animation-name" support. */ animation-name() vendor('animation-name', arguments) /* * Vendor "animation-duration" support. */ animation-duration() vendor('animation-duration', arguments) /* * Vendor "animation-delay" support. */ animation-delay() vendor('animation-delay', arguments) /* * Vendor "animation-direction" support. */ animation-direction() vendor('animation-direction', arguments) /* * Vendor "animation-iteration-count" support. */ animation-iteration-count() vendor('animation-iteration-count', arguments) /* * Vendor "animation-timing-function" support. */ animation-timing-function() vendor('animation-timing-function', arguments) /* * Vendor "animation-play-state" support. */ animation-play-state() vendor('animation-play-state', arguments) /* * Vendor "animation-fill-mode" support. */ animation-fill-mode() vendor('animation-fill-mode', arguments) /* * Vendor "hyphens" support. */ hyphens() vendor('hyphens', arguments, only: webkit moz ms official) /* * Vendor "appearance" support. */ appearance() vendor('appearance', arguments, only: webkit moz official) /* * Vendor "tab-size" support. */ tab-size() vendor('tab-size', arguments, only: moz o official) /* * Vendor "overflow-scrolling" support. */ overflow-scrolling() vendor('overflow-scrolling', arguments, only: webkit official) /* * Vendor "text-overflow" support, , -o- for opera 9.* - 10.* */ text-overflow() vendor('text-overflow', arguments, only: official o) /* * Vendor "text-size-adjust" support. */ text-size-adjust() vendor('text-size-adjust', arguments, only: official webkit ms) /* * Vendor "font-smoothing" support, webkit only. */ font-smoothing() vendor('font-smoothing', arguments, only: webkit) /* * Helper for border-radius(). */ -apply-border-radius(pos, importance) if length(pos) == 3 // border-radius: y = pos[0] x = pos[1] // We don't use moz for simple boder-radius anymore // vendor('border-radius-%s%s' % pos, pos[2], only: moz) vendor('border-%s-%s-radius' % pos, pos[2] importance, only: webkit official) else if pos[0] in (top bottom) // border-radius: -apply-border-radius(pos[0] left pos[1], importance) -apply-border-radius(pos[0] right pos[1], importance) else if pos[0] in (left right) // border-radius: unshift(pos, top); -apply-border-radius(pos, importance) pos[0] = bottom -apply-border-radius(pos, importance) /* * border-radius supporting vendor prefixes and * augmented behavior. * * Examples: * * border-radius: 2px 5px * border-radius: top 5px bottom 10px * border-radius: left 5px * border-radius: top left 5px * border-radius: top left 10px bottom right 5px * border-radius: top left 10px, bottom right 5px * */ border-radius() pos = () augmented = false importance = arguments[length(arguments) - 1] == !important ? !important : unquote('') for args in arguments for arg in args if arg is a 'ident' append(pos, arg) augmented = true else append(pos, arg) if augmented -apply-border-radius(pos, importance) pos = () vendor('border-radius', pos, only: webkit official) unless augmented /** * Vendor input-placeholder/placeholder support. * * Examples: * // Default syntax * body * placeholder(color #333, font-weight normal) * * // The comma is important * .placeholder-red * placeholder(color red,) * * // We can pass a function * green-placeholder() * color green * .placeholder-green * placeholder(green-placeholder) * * // We can pass a hash * textarea * placeholder((font-style italic) (font-weight bold) (padding '4px 10px')) */ placeholder() for v in ':-webkit-input' '-moz' ':-moz' '-ms-input' &:{v}-placeholder for pair in arguments if typeof(pair) == 'function' pair() else if pair is not null && pair[0] is not null {pair[0]}: type(pair[1]) == 'string' ? s(pair[1]) : pair[1] input-placeholder = placeholder /* * Vendor background support (gradients). */ background() if match('-gradient\(', ''+arguments) vendor('background', arguments, vendor-property: false) else background arguments background-image() if match('-gradient\(', ''+arguments) vendor('background-image', arguments, vendor-property: false) else background-image arguments cursor() if match('-gradient\(', ''+arguments) vendor('cursor', arguments, vendor-property: false) else cursor arguments list-style() if match('-gradient\(', ''+arguments) vendor('list-style', arguments, vendor-property: false) else list-style arguments list-style-image() if match('-gradient\(', ''+arguments) vendor('list-style-image', arguments, vendor-property: false) else list-style-image arguments ================================================ FILE: _stylus/_nib/lib/nib.js ================================================ /*! * nib * Copyright (c) 2010 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var stylus = require('stylus') , path = require('path') , nodes = stylus.nodes , utils = stylus.utils , Canvas exports = module.exports = plugin; // conditionally expose canvas-based APIs. try { Canvas = require('canvas'); var gradient = require('./nodes/gradient') , colorImage = require('./nodes/color-image') } catch (err) { // ignore } /** * Library version. */ exports.version = require(path.join(__dirname, '../package.json')).version; /** * Stylus path. */ exports.path = __dirname; /** * Return the plugin callback for stylus. * * @return {Function} * @api public */ function plugin() { return function(style){ style.include(__dirname); if (Canvas) { style.define('has-canvas', nodes.true); // gradients style.define('create-gradient-image', gradient.create) style.define('gradient-data-uri', gradient.dataURL) style.define('add-color-stop', gradient.addColorStop) // color images style.define('create-color-image', colorImage.create) style.define('color-data-uri', colorImage.dataURL); } else { style.define('has-canvas', nodes.false); } } } ================================================ FILE: _stylus/_nib/lib/nodes/color-image.js ================================================ /** * Module dependencies. */ var stylus = require('stylus') , Canvas = require('canvas') , nodes = stylus.nodes , utils = stylus.utils /** * Expose `ColorImage`. */ exports = module.exports = ColorImage; /** * Create a new `ColorImage` node with the given `color`. * * @param {Color} color node * @return {ColorImage} * @api public */ exports.create = function(color){ utils.assertColor(color); return new ColorImage(color); }; /** * Return the data URI for `colorImage`. * * @param {ColorImage} colorImage * @return {String} * @api public */ exports.dataURL = function(colorImage){ utils.assertType(colorImage, 'colorimage'); return new nodes.String(colorImage.toDataURL()); }; /** * Initialize a new `ColorImage` node with the given arguments. * * @param {Color} color node * @api private */ function ColorImage(color) { this.color = color; this.canvas = new Canvas(1, 1); this.ctx = this.canvas.getContext('2d'); this.ctx.fillStyle = color.toString(); this.ctx.fillRect(0, 0, 1, 1); }; /** * Inherit from `nodes.Node.prototype`. */ ColorImage.prototype.__proto__ = nodes.Node.prototype; /** * Inspect the color. * * @return {String} * @api private */ ColorImage.prototype.toString = function(){ return 'ColorImage(' + this.color.toString() + ')'; }; /** * Return data URI string. * * @return {String} * @api private */ ColorImage.prototype.toDataURL = function(){ return this.canvas.toDataURL(); }; ================================================ FILE: _stylus/_nib/lib/nodes/gradient.js ================================================ /** * Module dependencies. */ var stylus = require('stylus') , Canvas = require('canvas') , nodes = stylus.nodes , utils = stylus.utils; /** * Expose `Gradient`. */ exports = module.exports = Gradient; /** * Create a new `Gradient` node with the given `size` * and `start` position. * * @param {Number} size * @param {String|Ident|Literal} start * @return {Gradient} * @api public */ exports.create = function(size, start){ utils.assertType(size, 'unit', 'size'); utils.assertString(start, 'start'); return new Gradient(size.val, start.string); }; /** * Add color stop to `grad`. * * @param {Gradient} grad * @param {Unit} pos * @param {HSLA|RGBA} color * @return {Null} * @api public */ exports.addColorStop = function(grad, pos, color){ utils.assertType(grad, 'gradient', 'grad'); utils.assertType(pos, 'unit', 'pos'); utils.assertColor(color, 'color'); grad.addColorStop(pos.val / 100, color.rgba.toString()); return nodes.null; }; /** * Return the data URI for `grad`. * * @param {Gradient} grad * @return {String} * @api public */ exports.dataURL = function(grad){ utils.assertType(grad, 'gradient'); return new nodes.String(grad.toDataURL()); }; /** * Initialize a new `Gradient` node with the given `size` * and `start` position. * * @param {Number} size * @param {String} start * @api private */ function Gradient(size, start) { this.size = size; this.canvas = new Canvas(1, 1); this.setStartPosition(start); this.ctx = this.canvas.getContext('2d'); this.grad = this.ctx.createLinearGradient( this.from[0], this.from[1] , this.to[0], this.to[1]); }; /** * Inspect the gradient. * * @return {String} * @api private */ Gradient.prototype.toString = function(){ return 'Gradient(' + this.size + 'px ' + this.stops.map(function(stop){ return stop[0] + ' ' + stop[1]; }).join(', ') + ')'; }; /** * Set `start` position. * * @param {String} start * @api private */ Gradient.prototype.setStartPosition = function(start){ var size = this.size , canvas = this.canvas; switch (start) { case 'top': canvas.height = size; this.from = [canvas.width / 2, 0]; this.to = [canvas.width / 2, canvas.height]; break; case 'bottom': canvas.height = size; this.from = [canvas.width / 2, canvas.height]; this.to = [canvas.width / 2, 0]; break; case 'left': canvas.width = size; this.from = [0, 0]; this.to = [canvas.width, canvas.height]; break; case 'right': canvas.width = size; this.from = [canvas.width, canvas.height]; this.to = [0, 0]; break; default: throw new Error('invalid start position "' + start + '"'); } }; /** * Add color stop `pos` / `color`. * * @param {Number} pos * @param {String} color * @api private */ Gradient.prototype.addColorStop = function(pos, color){ this.grad.addColorStop(pos, color); }; /** * Return data URI string. * * @return {String} * @api private */ Gradient.prototype.toDataURL = function(){ var canvas = this.canvas , ctx = this.ctx; ctx.fillStyle = this.grad; ctx.fillRect(0, 0, canvas.width, canvas.height); return canvas.toDataURL(); }; /** * Inherit from `nodes.Node.prototype`. */ Gradient.prototype.__proto__ = nodes.Node.prototype; ================================================ FILE: _stylus/_nib/lib/nodes/vendor-helpers.js ================================================ var RE_GRADIENT_STOPS = /([\(\,]\s*)(-?(?:\d*\.)?\d+(?:%|px|em))(\s+)((hsl|rgb)a?\([^\)]+\)|#[^\)\,]+)/g , RE_GRADIENT_VAL = /(\(\s*)(?:(-?(\d*\.)?\d+)deg|((to )?(top|bottom|left|right)( (top|bottom|left|right))?))/g , RE_GRADIENT_TYPE = /((repeating-)?(linear|radial)-gradient\()/g , RE_TRANSFORM = /\b(transform)\b/g , RE_FILL_KEYWORD = /\s*\b(fill)\b\s*/g; var DIRECTIONS = { top: 'bottom', bottom: 'top', left: 'right', right:'left' }; /** * Expose `normalize`. */ function normalize(property, value, prefix){ var result = value.toString() , args; /* Fixing the gradients */ if (~result.indexOf('gradient(')) { /* Normalize color stops */ result = result.replace(RE_GRADIENT_STOPS,'$1$4$3$2'); /* Normalize legacy gradients */ result = result.replace(RE_GRADIENT_VAL, function(){ args = [].slice.call(arguments, 1); return normalizeGradient(args, prefix); }); /* Adding prefixes to the legacy gradients */ if (prefix) result = result.replace(RE_GRADIENT_TYPE, '-' + prefix + '-$1'); } /* Adding prefixes to the `transform` values of legacy `transition` property */ if (prefix && (property == "'transition'" || property == "'transition-property'")) { result = result.replace(RE_TRANSFORM, '-' + prefix + '-$1'); } /* Removing `fill` keyword from the legacy `border-image` property */ if (prefix && (property == "'border-image'" || property == "'border-image-slice'")) { result = result.replace(RE_FILL_KEYWORD, ' '); } return result; } function normalizeGradient(parts, prefix){ /* Fix the degrees to the legacy syntax */ var val = parts[0]; // when the gradients were unprefixed, the w3c changed the way that the // angle direction is interpreted. see: // http://blogs.msdn.com/b/ie/archive/2012/06/25/unprefixed-css3-gradients-in-ie10.aspx if (parts[1]) val += (prefix ? parseFloat((Math.abs(450 - parts[1]) % 360).toFixed(3)) : parts[1]) + 'deg'; /* Fix the directions to the legacy syntax */ if (prefix && parts[4]) { // `to top` to `bottom` etc. if (parts[5]) val += DIRECTIONS[parts[5]]; if (parts[6]) val += ' ' + DIRECTIONS[parts[7]]; } else if (!prefix && !parts[4]) { // `top` to `to bottom` etc. if (parts[5]) val += 'to ' + DIRECTIONS[parts[5]]; if (parts[6]) val += ' ' + DIRECTIONS[parts[7]]; } else { if (parts[3]) val += parts[3]; } return val; } var plugin = function(){ return function(style){ var nodes = this.nodes; style.define('normalize', function(property, value, prefix) { return new nodes.Ident(normalize(property, value, prefix)); }); }; }; module.exports = plugin; ================================================ FILE: _stylus/main.styl ================================================ @import '_nib' iconic-stroke('../iconic/') /** * Variables */ shadow__tiny = 0 1px 1px rgba(0,0,0,.15) shadow__pressed_effect = 0 1px 0 rgba(255,255,255, .5) font__base = Georgia, 'Times New Roman', serif font__size = 18px font__size_small = 14px color__bg = #F4F4F4 color__text = #404040 color__text_second = #888 color__link = #0066CC color__link_visited = #550066 color__blue = #B4D2F0 color__blue_dark = #99B3CC color__yellow = #F0E9B4 color__yellow_dark = #CCC699 img__noise = url('/assets/img/noise.png') * box-sizing: border-box html body margin 0 height 100% html font-family font__base font-size font__size body reset-body() background img__noise color__bg color color__text text-shadow shadow__pressed_effect h1 h2 h3 h4 h5 reset-font() reset-box-model() margin-bottom .5em small font-size .6em color color__text_second h1 font-size 2em h2 font-size 1.8em h3 font-size 1.3em h4 font-size 1.2em h5 font-size 1em font-weight bold ul ol margin 1em 0 a // display inline-block padding: 0.6em; // Увеличение зоны нажатия margin: -0.6em; // для планшетов color color__link text-decoration none &:hover text-decoration underline &:visited color color__link_visited &.ignore-visited color color__link .wrapper width 720px margin 0 auto padding 4em 0 1em position relative min-height 100% .widget size 728px 90px margin-bottom 1em background-color #fff box-shadow shadow__tiny .lwidget size 160px 600px // background-color #fff absolute left 100% margin-left 36px // box-shadow shadow__tiny .money .money__wrapper width 508px height 105px margin 0 auto .money margin-bottom 1em .footer // absolute bottom 0 padding-bottom 1em font-size .8em line-height 1.5 .license color color__text_second a[rel=license] vertical-align middle .intro margin-bottom 1em font-size font__size_small color color__text_second .unpublished-post color color__text_second .content margin 2em 0 2em line-height 1.5 position relative img box-shadow shadow__tiny .prevnext margin 2em 0 position relative clearfix() .prevnext__previous-arrow .prevnext__next-arrow position absolute font-size 1rem .prevnext__previous-arrow left -1.5em .prevnext__next-arrow right -1.5em .prevnext__previous .prevnext__next width 40% float left .prevnext__next float right text-align right .message padding .5em 1em box-shadow shadow__tiny background img__noise color__blue border color__blue_dark &.info background-color color__yellow border color__yellow_dark code background-color rgba(255,255,255,.5); .sidenote absolute left 100% width 240px margin-left 1.5em padding 1em font-size font__size_small background-color #fff box-shadow shadow__tiny .num color color__text_second .page-header__title a margin 0 padding 0 .page-header__rss-link font-family "IconicStroke" display block absolute right 0 font-size 1.4em color color__text text-shadow none margin 0 padding 0 &:visited color color__text &:hover color color__text text-decoration none .page-header__twitter-link display block size 20px margin 0 padding 0 absolute right 2em background url('/assets/img/twitter-icon.png') background-size cover .page-header__title-link color color__text &:visited color color__text &:hover color color__link code background-color #FFF box-shadow shadow__tiny font-size 15px pre padding 18px 18px overflow scroll background-color #fff box-shadow shadow__tiny code font-family Consolas, Monaco, 'Andale Mono', monospace padding 4px 4px 2px pre > code padding 0 background-color transparent box-shadow none .prose font-style italic font-size 15px margin 29px 0 display block .lazada-banner display block width 720px height 120px background url('/assets/img/lazada.jpg'); margin 0 0 2rem 0; box-shadow shadow__tiny ================================================ FILE: assets/css/main.css ================================================ @font-face { font-family: 'IconicStroke'; src: url("../iconic//iconic_stroke.eot"); src: local('☺'), url("../iconic//iconic_stroke.ttf") format('truetype'), url("../iconic//iconic_stroke.svg#iconic") format('svg'); font-weight: normal; font-style: normal; } /** * Variables */ * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html, body { margin: 0; height: 100%; } html { font-family: Georgia, 'Times New Roman', serif; font-size: 18px; } body { line-height: 1; color: #000; background: #fff; background: url("/assets/img/noise.png") #f4f4f4; color: #404040; text-shadow: 0 1px 0 rgba(255,255,255,0.5); } h1, h2, h3, h4, h5 { font-weight: inherit; font-style: inherit; font-family: inherit; font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0; outline: 0; margin-bottom: 0.5em; } h1 small, h2 small, h3 small, h4 small, h5 small { font-size: 0.6em; color: #888; } h1 { font-size: 2em; } h2 { font-size: 1.8em; } h3 { font-size: 1.3em; } h4 { font-size: 1.2em; } h5 { font-size: 1em; font-weight: bold; } ul, ol { margin: 1em 0; } a { padding: 0.6em; margin: -0.6em; color: #06c; text-decoration: none; } a:hover { text-decoration: underline; } a:visited { color: #506; } a:visited.ignore-visited { color: #06c; } .wrapper { width: 720px; margin: 0 auto; padding: 4em 0 1em; position: relative; min-height: 100%; } .widget { width: 728px; height: 90px; margin-bottom: 1em; background-color: #fff; -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.15); box-shadow: 0 1px 1px rgba(0,0,0,0.15); } .lwidget { width: 160px; height: 600px; position: absolute; left: 100%; margin-left: 36px; } .money .money__wrapper { width: 508px; height: 105px; margin: 0 auto; } .money { margin-bottom: 1em; } .footer { padding-bottom: 1em; font-size: 0.8em; line-height: 1.5; } .license { color: #888; } .license a[rel=license] { vertical-align: middle; } .intro { margin-bottom: 1em; font-size: 14px; color: #888; } .unpublished-post { color: #888; } .content { margin: 2em 0 2em; line-height: 1.5; position: relative; } .content img { -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.15); box-shadow: 0 1px 1px rgba(0,0,0,0.15); } .prevnext { margin: 2em 0; position: relative; zoom: 1; } .prevnext:before, .prevnext:after { content: ""; display: table; } .prevnext:after { clear: both; } .prevnext__previous-arrow, .prevnext__next-arrow { position: absolute; font-size: 1rem; } .prevnext__previous-arrow { left: -1.5em; } .prevnext__next-arrow { right: -1.5em; } .prevnext__previous, .prevnext__next { width: 40%; float: left; } .prevnext__next { float: right; text-align: right; } .message { padding: 0.5em 1em; -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.15); box-shadow: 0 1px 1px rgba(0,0,0,0.15); background: url("/assets/img/noise.png") #b4d2f0; border: 1px solid #99b3cc; } .message.info { background-color: #f0e9b4; border: 1px solid #ccc699; } .message code { background-color: rgba(255,255,255,0.5); } .sidenote { position: absolute; left: 100%; width: 240px; margin-left: 1.5em; padding: 1em; font-size: 14px; background-color: #fff; -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.15); box-shadow: 0 1px 1px rgba(0,0,0,0.15); } .sidenote .num { color: #888; } .page-header__title a { margin: 0; padding: 0; } .page-header__rss-link { font-family: "IconicStroke"; display: block; position: absolute; right: 0; font-size: 1.4em; color: #404040; text-shadow: none; margin: 0; padding: 0; } .page-header__rss-link:visited { color: #404040; } .page-header__rss-link:hover { color: #404040; text-decoration: none; } .page-header__twitter-link { display: block; width: 20px; height: 20px; margin: 0; padding: 0; position: absolute; right: 2em; background: url("/assets/img/twitter-icon.png"); -webkit-background-size: cover; -moz-background-size: cover; background-size: cover; } .page-header__title-link { color: #404040; } .page-header__title-link:visited { color: #404040; } .page-header__title-link:hover { color: #06c; } code { background-color: #fff; -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.15); box-shadow: 0 1px 1px rgba(0,0,0,0.15); font-size: 15px; } pre { padding: 18px 18px; overflow: scroll; background-color: #fff; -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.15); box-shadow: 0 1px 1px rgba(0,0,0,0.15); } code { font-family: Consolas, Monaco, 'Andale Mono', monospace; padding: 4px 4px 2px; } pre > code { padding: 0; background-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .prose { font-style: italic; font-size: 15px; margin: 29px 0; display: block; } .lazada-banner { display: block; width: 720px; height: 120px; background: url("/assets/img/lazada.jpg"); margin: 0 0 2rem 0; -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.15); box-shadow: 0 1px 1px rgba(0,0,0,0.15); } ================================================ FILE: assets/css/print.css ================================================ @media print { /* !Убираем украшения для страницы */ body { background: #FFF; text-shadow: none; } /* !Переводим локальные ссылки в читаемый вид */ a::after { content: " (http://largescalejs.ru" attr(href) ")"; } /* !Ссылки в заголовках делаем маленькими */ .page-header__title-link::after { font-size: 14px; } /* !Переводим все остальные ссылки в читаемый вид */ a[href^=http]::after { content: " (" attr(href) ")"; } /* !Используем всю ширину листа */ .wrapper { margin: 0; padding: 0; width: 100%; } /* !Чтобы после статьи не было много пустого места, уменьшаем отступ */ .content { margin-bottom: 1em; } /* !Убираем ненужные украшения для тегов в статьях, добавляем выделение */ p > code { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; font-weight: bold; } /* !Это нужно для того, чтобы код не был на двух страницах */ .highlight > pre { page-break-inside: avoid; } /* !Убираем абсолютное позиционирование для футера */ .footer { position: relative; padding: 0; } /* !Прячем всё, что не несет смысловой нагрузки | !important для social-likes */ .github__badge, .page-header > h4, .page-header__rss-link, .page-header__twitter-link, .social-likes, .widget, .money, #disqus_thread, .message, .license, .prevnext, .footer > p > span, .footer > p > a:last-child { display: none !important; } } ================================================ FILE: assets/css/pygments.css ================================================ .highlight { background: #ffffff; } .highlight .c { color: #999988; font-style: italic } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { font-weight: bold } /* Keyword */ .highlight .o { font-weight: bold } /* Operator */ .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #999999 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { font-weight: bold } /* Keyword.Constant */ .highlight .kd { font-weight: bold } /* Keyword.Declaration */ .highlight .kp { font-weight: bold } /* Keyword.Pseudo */ .highlight .kr { font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #009999 } /* Literal.Number */ .highlight .s { color: #d14 } /* Literal.String */ .highlight .na { color: #008080 } /* Name.Attribute */ .highlight .nb { color: #0086B3 } /* Name.Builtin */ .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ .highlight .no { color: #008080 } /* Name.Constant */ .highlight .ni { color: #800080 } /* Name.Entity */ .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ .highlight .nn { color: #555555 } /* Name.Namespace */ .highlight .nt { color: #000080 } /* Name.Tag */ .highlight .nv { color: #008080 } /* Name.Variable */ .highlight .ow { font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #009999 } /* Literal.Number.Float */ .highlight .mh { color: #009999 } /* Literal.Number.Hex */ .highlight .mi { color: #009999 } /* Literal.Number.Integer */ .highlight .mo { color: #009999 } /* Literal.Number.Oct */ .highlight .sb { color: #d14 } /* Literal.String.Backtick */ .highlight .sc { color: #d14 } /* Literal.String.Char */ .highlight .sd { color: #d14 } /* Literal.String.Doc */ .highlight .s2 { color: #d14 } /* Literal.String.Double */ .highlight .se { color: #d14 } /* Literal.String.Escape */ .highlight .sh { color: #d14 } /* Literal.String.Heredoc */ .highlight .si { color: #d14 } /* Literal.String.Interpol */ .highlight .sx { color: #d14 } /* Literal.String.Other */ .highlight .sr { color: #009926 } /* Literal.String.Regex */ .highlight .s1 { color: #d14 } /* Literal.String.Single */ .highlight .ss { color: #990073 } /* Literal.String.Symbol */ .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #008080 } /* Name.Variable.Class */ .highlight .vg { color: #008080 } /* Name.Variable.Global */ .highlight .vi { color: #008080 } /* Name.Variable.Instance */ .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ ================================================ FILE: assets/vendor/social-likes/social-likes.css ================================================ /*! Social Likes v3.0.1 by Artem Sapegin - http://sapegin.github.com/social-likes - Licensed MIT */ .social-likes,.social-likes__widget{display:inline-block;padding:0;vertical-align:middle!important;word-spacing:0!important;text-indent:0!important;list-style:none!important}.social-likes{opacity:0}.social-likes_visible{opacity:1;-webkit-transition:opacity .1s ease-in;transition:opacity .1s ease-in}.social-likes>*{display:inline-block;visibility:hidden}.social-likes_vertical>*{display:block}.social-likes_visible .social-likes__widget{visibility:inherit}.social-likes__widget{display:inline-block;position:relative;white-space:nowrap}.social-likes__widget:before,.social-likes__widget:after{display:none!important}.social-likes_vertical .social-likes__widget{display:block;float:left;clear:left}.social-likes__button,.social-likes__icon,.social-likes__counter{text-decoration:none;text-rendering:optimizeLegibility}.social-likes__button,.social-likes__counter{display:inline-block;margin:0;outline:0}.social-likes__button{position:relative;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.social-likes__button:before{content:"";display:inline-block}.social-likes__icon{position:absolute;top:0;left:0}.social-likes__counter{display:none;position:relative}.social-likes_ready .social-likes__counter,.social-likes__counter_single{display:inline-block}.social-likes_ready .social-likes__counter_empty{display:none}.social-likes_vertical .social-likes__widget{display:block}.social-likes_notext .social-likes__button{padding-left:0}.social-likes_single-w{position:relative;display:inline-block}.social-likes_single{position:absolute;text-align:left;z-index:99999;visibility:hidden;opacity:0;-webkit-transition:visibility 0 .3s,opacity .3s ease-out;transition:visibility 0s .3s,opacity .3s ease-out;-webkit-backface-visibility:hidden;backface-visibility:hidden}.social-likes_single.social-likes_opened{visibility:visible;opacity:1;-webkit-transition:opacity .3s ease-out;transition:opacity .3s ease-out}.social-likes__button_single{position:relative}.social-likes,.social-likes__widget{border:0;font-size:14px}.social-likes__widget{line-height:20px}.social-likes{min-height:28px;margin:-3px}.social-likes,.social-likes_single-w{line-height:20px}.social-likes__widget{margin:3px}.social-likes__button,.social-likes__counter{font-family:"Trebuchet MS","Helvetica Neue",Tahoma,sans-serif;font-size:14px;line-height:18px;border-width:1px;border-style:solid;border-radius:3px}.social-likes__button{padding:1px 4px 1px 20px;font-weight:700;text-shadow:0 1px 0 rgba(255,255,255,.6);box-shadow:0 1px 1px rgba(0,0,0,.05);-webkit-transition:border .1s ease-in-out,color .2s ease-in-out;transition:border .1s ease-in-out,color .2s ease-in-out}.social-likes__icon{width:20px;height:20px;background-repeat:no-repeat}.social-likes__counter{margin-left:7px;padding:1px 4px;font-weight:400;color:#666;color:rgba(0,0,0,.5);cursor:default}.social-likes__counter:before,.social-likes__counter:after{content:"";position:absolute;width:0;height:0}.social-likes__counter:before{top:4px;left:-6px;border:6px inset transparent;border-left:0;border-right:6px solid;border-right-color:inherit;opacity:.7}.social-likes__counter:after{top:5px;left:-4px;border:5px inset transparent;border-left:0;border-right:5px solid}.social-likes_vertical{margin:-6px -4px}.social-likes_vertical .social-likes__widget{margin:6px 4px}.social-likes_notext .social-likes__widget{margin:3px 2px}.social-likes_notext .social-likes__button{width:16px}.social-likes_single{margin-top:-16px;padding:6px 6px 4px;background:#fff;box-shadow:0 0 10px rgba(0,0,0,.25)}.social-likes__widget_single{margin:0}.social-likes__button_single{padding-left:19px;background:#e2e2e2;background:-webkit-linear-gradient(top,#f7f7f7,#e2e2e2);background:linear-gradient(top,#f7f7f7,#e2e2e2);color:#444;border-color:#ccc;border-color:rgba(179,179,179,.8);border-bottom-color:rgba(153,153,153,.8)}.social-likes__button_single:hover,.social-likes__widget_active .social-likes__button_single{background:#f4f4f4;background:-webkit-linear-gradient(top,#f0f0f0,#cfcfcf);background:linear-gradient(top,#f0f0f0,#cfcfcf);color:#222;border-color:#bbb;border-bottom-color:#9f9f9f}.social-likes__icon_single{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAABxVBMVEVyqHIsfCwneyc3iDctfi0sfiwAAABupm5pomlxp3FwqXB0q3RSllI0hTRAjEBXmVdJkklig2JspGx1qHVdnF2LrYv///9wrXBQllBvrW////9cnVxmiGZtpG11p3WNrY1fnV95sHkufi4xgjElYyUwiTAreSseTh4ugS4eUh4kZSQvfi8seiwwiDAxgzH///9tqW1mpmYggiC827wlhSXr9Os5jznk8OS62rqp0qk5lzk6mDoUhRQ0lTRwsXBfpV81izVkqGRYoVhoqWhfpF9Vo1VaoVppqWlgpWAwiTAyjDI6jzpBlEFkqmRtrG1bpltysHJFlkVZpVkkiCQ4jjgniScVexWSxZIxjTExhjEpiikyiDI0izQxiTEvii+m0KaNvo2FuoV+tn7c7NxIl0hurW6kzqSn0Kd5tXl7t3s4lDiEvoQziDMxiDFUpFRHm0f7/Pvi8OI6lTo9mD3i8eKy17JwsnB+u35NoU1JnUnT5tP8/fyn0ac8mDyq0qpPo09eql5AmkBDnENFnkVhrGE5mDnP5s8+mz4qjyq017QhiiHB38FHnkdJoElaqFrU6dQtkS2z1rOezJ7V6dXC38KfzZ/O5s44zy9hAAAAL3RSTlPu6ebu5uYA7u7w7u3w7e7w73TH2fGAAu3v7QPtfcjbhPHtyu1u5s1u7XZxzM7m7ed67K8AAADZSURBVAjXYxATFeZkYOfgYGfglJOXYhAUN3D3S0pMCLB3NpCUYRAy9PA11gcCYztHQ1kGETf/ZH1zS0tz/XBXFwUGHh8L/dJma+v68rwgJ34GLlN908a+yf0tTRX5gQIM3Pr6dR1T9PW7WxvKUiUY+FIqe2wn6ut3dlno6ysyMIcU1U6wmjrJ1qq6IF6agdc7q7im18amvUo/LUyDQdkhKltff1qbvn56aIweg1K0V0Rmib5+YUZwXI42g7pqpKeRiZmZiVFsrq4mA5uKGiMLKxMTKwujjhYbAFshM23jReW7AAAAAElFTkSuQmCC");background-position:2px 3px}.social-likes__counter_single{background:#f6f6f6;border-color:#ddd}.social-likes__counter_single:after{border-right-color:#f6f6f6}.social-likes__button_facebook{padding-left:19px;background:#eceef5;background:-webkit-linear-gradient(top,#fff,#d8def4);background:linear-gradient(top,#fff,#d8def4);color:#3b5998;border-color:#cad4e7;border-color:rgba(202,212,231,.8);border-bottom-color:rgba(189,194,203,.8)}.social-likes__button_facebook:hover{background:#c0cdf3;background:-webkit-linear-gradient(top,#f2f3f7,#c0cdf3);background:linear-gradient(top,#f2f3f7,#c0cdf3);color:#253860;border-color:#b4bfd4;border-bottom-color:#b3b7bf}.social-likes__icon_facebook{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOBAMAAADtZjDiAAAAFVBMVEU7WZj///9thLTr7vRheaxFYp5geKvYYUakAAAAL0lEQVQI12NwYAABFgZkkCwIoQ2hNJSC0YKCEIaxoDFWeTAdpKSkJKikCqeh9gAAtSUEXqOhTAgAAAAASUVORK5CYII=");background-position:3px 3px}.social-likes__counter_facebook{background:#f2f3f7;border-color:#cad4e7}.social-likes__counter_facebook:after{border-right-color:#f2f3f7}.social-likes__button_twitter{padding-left:19px;background:#d5e6ef;background:-webkit-linear-gradient(top,#fff,#d5e6ef);background:linear-gradient(top,#fff,#d5e6ef);color:#186487;border-color:#a4cce5;border-color:rgba(164,204,229,.8);border-bottom-color:rgba(158,186,204,.8)}.social-likes__button_twitter:hover{background:#bfdfed;background:-webkit-linear-gradient(top,#f2f8fc,#bfdfed);background:linear-gradient(top,#f2f8fc,#bfdfed);color:#0b3752;border-color:#9cbbcf;border-bottom-color:#68a0c4}.social-likes__icon_twitter{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAALCAMAAAB4W0xQAAAA3lBMVEUAAAD///8lseQltOvl9fr///////////8Af68Af5+Ay+gAf7QZmMglrd9Pwe0Ag7UBiL0ChLgMqOW03u8FgbAVnc96x+QlncoAfK8AgbP///8MrOrV7/hoxukAfa2N1fEjsOZBvOua1u4Afa+44/P///+HzOZsyetsy+4AgLBsxOgAiLy/5PEAfKwCg7YRiLQKmM+LyuIAhbkArO4Aq+0Aq+wAlc4AqOgAp+cAntoAjMIAqekAod4Am9cAmNIAo+EApuYApeUApOMApeQAjsQAod8AkckAqusAnNgAouBS5YTcAAAAM3RSTlMASPPzoXckDBAQwTCf8+ef8NH7pLPRueBgnzz7x9lw1/Hry4CuMIjf35/f78Fg4Wf5ar/hKdysAAAAhElEQVQIHR3BhQKCMBQF0KsCG3a3Yne/bYTd+v8/5PAcwEaIt/tL2DZQrELLPV7BbrUGKkGNI6HO9+dnswXKvspmkgdBwutZQOl68U9Hj4hudQCFvJCuK4lECprVatDfPo7QqCNJE9EIQsNBVxKJmAGNT+fq+yaZNhg0Zi5m44nTNBm0H8LlEYm9SraOAAAAAElFTkSuQmCC");background-position:3px 5px}.social-likes__counter_twitter{background:#f2f8fc;border-color:#a4cce5}.social-likes__counter_twitter:after{border-right-color:#f2f8fc}.social-likes__button_plusone{padding-left:12px;background:#e4e4e4;background:-webkit-linear-gradient(top,#f5f5f5,#e4e4e4);background:linear-gradient(top,#f5f5f5,#e4e4e4);color:#da573b;border-color:#bbb;border-color:rgba(204,204,204,.8);border-bottom-color:rgba(179,179,179,.8)}.social-likes__button_plusone:hover{background:#f4f4f4;background:-webkit-linear-gradient(top,#f9f9f9,#f0f0f0);background:linear-gradient(top,#f9f9f9,#f0f0f0);color:#cd4427;border-color:#ddd;border-bottom-color:#ccc}.social-likes__icon_plusone{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAPCAMAAADqIa48AAAA1VBMVEUAAAD29vbeaU/w6uj6+vrs5uXijXvv7+/aVzvaVzr77eru6urutKfdZUvggm7aWT3linf8/PzfeGLp5eTnoJHmqp3hiHXhd2DxysLx6ef02NL39PTcalHcYUbcYUXaWTz219HccVnl1tP33dfkraHfbVPo4+LnlILt6encaE/cYkfkzsr57OrbWz/gfGbsq53fiXbmu7LgkH7q4d/48fDcdFzssqXqp5jl2dbcZ03j09DecVjebVXdclv4+PjdemTw8PD44dzik4LcmozjgWvgdFzxxLrCp/DQAAAACHRSTlMA////////+EuuwZEAAAB3SURBVAgdLcEFFoIAEEDBDwLu2t3d3d19/yOJD2dIpjMJcTk+SikrHpJwwIJKEYhJBGjjykk0COMekBexQeeALVlAj0tolQvAS/eL2aCJ63L/nHSK5/bWSQfPRrXeAJ7XtdOtan8Ij5VlGDVTz+DnZ2Tqgb+t7r4MkwpuQ8fKEQAAAABJRU5ErkJggg==");background-position:0 6px}.social-likes__counter_plusone{background:#f9f9f9;border-color:#d2d2d2}.social-likes__counter_plusone:after{border-right-color:#f9f9f9}.social-likes__button_mailru{padding-left:18px;background:#004584;background:-webkit-linear-gradient(top,#5d90ba,#004584);background:linear-gradient(top,#5d90ba,#004584);color:#fff;color:rgba(255,255,255,.95);border-color:#1e65a5;border-color:rgba(11,84,153,.8);border-bottom-color:rgba(3,27,48,.8);text-shadow:0 -1px 0 rgba(0,0,0,.3)}.social-likes__button_mailru:hover{background:#001e5d;background:-webkit-linear-gradient(top,#618cae,#001e5d);background:linear-gradient(top,#618cae,#001e5d);color:#fff;color:rgba(255,255,255,.99);border-color:#094984;border-bottom-color:#031b30}.social-likes__icon_mailru{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAABhlBMVEUAAAD0xQD/xgBcSwAAAAD8ywC1kQD7yQC5lAD/1AAAAABsVwD/zAD6yAD5yAD/xgAAAAAAAAAAAAAQDgAoIAAAAABFLgC/mQD/yADcsQAAAAAsIwAAAAAAAAD4xwAAAAA7MAD6yQBbSQDZrQDcsgDywQDxwAClggD/yADnuAAAAAD8zAD7ygDargD8zAAzKQATEAD8ywBHOQBAMgAtJAD7ygD6yQCMcACLbwAPDACigQCphwBiUAC3kgDZrgAAAADIogAAAAAAAAD/zABmTwAAAAAGBgBZSADrvACBaAAAAADTqgD8ygDcsgD5yQD8ywD8ywDywQDqvQDRqQDpvADktwCyjgAAAAA8MADouQCzkADuvgDuwADEnQD2xQD9ywD7yQAAAADougDbsgBYRAC/mQCgfwDZrgDnvgDGoACYegChgAA+MADIogAAAADsvgDtvgDzwgB4XgCqiAD6yADHnwDNpQBpUwClhADmuQB8YwDzwwCHawCffQD0wwDktwDAnAD9ywDU1CnPAAAAgXRSTlMA+AmjBdjG/WcMZKke7PkSCGkCiV4DC8kq4wtWEA7pUXPgltnm9fJKIUxTeNnYaZVPz5JHdvewt25mwcVifdtuxCZhLS2HcaLfqYGT4W72wO2m4tyl0r9HmLSY8m/UmpniCrZdNNFWqDdnkF8lXiDtve1GZ/5uWJi8SaT2a0Oro2IKDPtyAAAAy0lEQVQIHQXBA0NDYQCG0Wf87h2zrWXXss1l215tme8/7xwAgAAAAOBsbJHkqbMBoLpeDT3R3gEdjgKES6tc3V39fT7/QzAA7JuyiEfSQfz18wecob2xYHnTuOtRf28mzKWud01JAfcfsr70zp1uLnYKgSdZ33rmXFfbxgZOZL0owa1jq62y0yanSNZv8hS3VzWty4op23EcOjuC5oqO+YnJ6c1ir8nNADLbVzS3Oru0mJ+emgKQN+JfX1uYGspyAwAMb/hmBmvTAP4BUaQsfohkhJwAAAAASUVORK5CYII=");background-position:1px 2px}.social-likes__counter_mailru{background:#fff1c2;border-color:#ffc70d}.social-likes__counter_mailru:after{border-right-color:#fff1c2}.social-likes_notext .social-likes__icon_mailru{background-position:2px 2px}.social-likes__button_vkontakte{background:#436f96;background:-webkit-linear-gradient(top,#8faecf,#436f96);background:linear-gradient(top,#8faecf,#436f96);color:#fff;color:rgba(255,255,255,.95);border-color:#4d84c1;border-color:rgba(78,131,193,.8);border-bottom-color:rgba(52,88,127,.8);text-shadow:0 -1px 0 rgba(0,0,0,.3)}.social-likes__button_vkontakte:hover{background:#4e80ab;background:-webkit-linear-gradient(top,#a2c0df,#4e80ab);background:linear-gradient(top,#a2c0df,#4e80ab);color:#fff;color:rgba(255,255,255,.99);border-color:#5788be;border-bottom-color:#3b6798}.social-likes__icon_vkontakte{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAMAAADH72RtAAABI1BMVEUeRmv6+/weRGn09vicrb0cRGsAAADu8fS9x9IgQWrg5usgRGsfQ2ocQmcqVX8cOHEiRG4AP38dQW0iRWghQ2scRWgfRGsdRmv29/keR2vW3eUkSG0fRmn6+vsbRG5wiJ8hRmySpLcAf39eeZSruMfe4+pNaokAAAAXRXOYqrodR2yRpLedrr4fP2qQo7UfRWqer7/n6u78/f3o7PAzM2aNoLTEz9l4jqbz9fbN1d21wc8/X4AkTHFyiaJ0jKMcRnGywM0fRWseRGsxV3kgRWqDma4eRGogRWodRGofRWxRbouHmq4eQ2zt8PPW3eUfQ2tEY4W2ws5cd5J5j6eltcRogJxNbIqUprhwhaCvvMtMa4qEma3Gz9k6Wn9aeJIfRGr////flzUiAAAAYHRSTlNM/Uv4uy0A89Uf60dIGwYJHgQjFiYsYUX5MuUVQfslfTakAmnA6FkBC6w9o7oYnzC57v7vBZnagfff0X1ho60SzIB0Q2e0ZT5WSTV1RPLQQFq+Qn6qXYGbVJ45aswwV0ob93UPAAAAtUlEQVQY02NgE5SWFGdiAANuBV4+NgZ1XnkpiQQIYFTVExFk4FdjTkAAI3N7BmHtBGTgIMQgYACk2R09xNxsDYEsDlEGThaQCLdXpLe7HUiEFSriGZHg5+OKLMIdGxwQ54sswhMVEu4fxowkEh+akBDNY4msJigm0NnGGCKiAxJxYhJzsTaD2K6rCXI+FxeXBdiFViIMGsqMyG42FWZQElJEeExWRd+EgY1PQAsaGAwMMnL8bAARvTwsqGgkuAAAAABJRU5ErkJggg==");background-position:1px 2px}.social-likes__counter_vkontakte{background:#eaeef3;border-color:#afc1d4}.social-likes__counter_vkontakte:after{border-right-color:#eaeef3}.social-likes__button_odnoklassniki{padding-left:17px;background:#f6900b;background:-webkit-linear-gradient(top,#f6bb6d,#f6900b);background:linear-gradient(top,#f6bb6d,#f6900b);color:#fff;color:rgba(255,255,255,.95);border-color:#d99c27;border-color:rgba(217,154,38,.8);border-bottom-color:rgba(197,137,7,.8);text-shadow:0 -1px 0 rgba(0,0,0,.12)}.social-likes__button_odnoklassniki:hover{background:#f69a21;background:-webkit-linear-gradient(top,#fbcc5a,#f69a21);background:linear-gradient(top,#fbcc5a,#f69a21);color:#fff;color:rgba(255,255,255,.99);border-color:#f0b22c;border-bottom-color:#c59121}.social-likes__icon_odnoklassniki{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAPCAMAAAABFhU/AAAAt1BMVEUAAAD9/f37+/sAAAAAAABYWFgAAAD19fUAAAD///8AAAD////z8/P9/f3///////9ycnK/v7/////9/f37+/v19fV3d3fa2tr////////BwcHh4eGlpaXHx8f///+srKz////5+fn4+Pj///94eHj5+fltbW35+fns7Oz19fX9/f3////q6ur9/f3S0tL////8/Pynp6fl5eX////9/f2bm5v9/f3s7Oz////////s7OwAAAD///+3C/ewAAAAPHRSTlMA5e0eBCsj0QIDHQ/J6F0SKGhR9/DUQA4h/GsRJXMnKPZWU/MzrDGvr5+j0q2gf8nyUjzw8Urusqt1RBmYPbn7AAAAd0lEQVQIHQXBBQKCQABFwYcgu4DSdnd367//uZwBgvyTBwDJSdIuAQ4qX6W2wF2X30MFcNaVrwpgr9v7qTWQGkkmhWC6zI7ZfBIQa7baLMaK6dckSWaEF3YlDVwP2o46qg8hctRoGtUjWhVT9aq1Sg/ruxYb+vYPq3IOiMcZvH4AAAAASUVORK5CYII=");background-position:4px 3px}.social-likes__counter_odnoklassniki{background:#ffe9be;border-color:#d9ab53}.social-likes__counter_odnoklassniki:after{border-right-color:#ffe9be}.social-likes_notext .social-likes__icon_odnoklassniki{background-position:5px 3px}.social-likes__button_pinterest{padding-left:20px;background:#eee;background:-webkit-linear-gradient(top,#fefefe,#d3d3d3);background:linear-gradient(top,#fefefe,#d3d3d3);color:#c71a28;border-color:#bbb;border-color:rgba(186,186,186,.8);border-bottom-color:rgba(153,153,153,.8)}.social-likes__button_pinterest:hover{background:#efefef;background:-webkit-linear-gradient(top,#fff,#d9d9d9);background:linear-gradient(top,#fff,#d9d9d9);color:#c11524;border-color:rgba(186,186,186,.6);border-bottom-color:rgba(153,153,153,.6)}.social-likes__icon_pinterest{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAARCAMAAADjcdz2AAABYlBMVEUAAADBByTBAB62ACTBByS5ABe/ABXBBiPBBiTBByO/AB/ABiS/AAC0AB7BBiPBBiO+BCDBByTBBSMAAADBBiPBBCPBBiTBBiTBByTBBiPABiO+ByK8BSG/BCLBByO/AyPABiLBBSPBByTAByPABSPABiPABCPBBiO/AyPBBSPAByTBBiLBBiTBBiLABiSqAADBBiTBByPCACTABiTBByTBBSPBBiO9BSDABSTBBiTBBiTBByTABiPBBSPBAyPABiTBBiPBByTABSTBByS/BCS7ABrBBiPBBiTAByO5ACLBByPBBySyABnBBiPABiLABSJ/AADBBiPAByTBBiPBBiTBByTBBSPBBiS/BSLBByTCByO/Bh/ABiTDByTCByTABiS/BiTBBiOZAAC/BCLBByTBBSTABiTBBSS6AB3AByTBBiTBByTBByS4ABy/AB/AByTBBiTBBSPCByXCCCXCByTDCCV4snBOAAAAcnRSTlMA7BkH6wsM6rylEOYEEZbPN42zAew6/ZnGo5dDLjSjQCXXooJeeznLSKtF2/RLTQbs1RXerdlPL1qhqrGcMkdveemwkWkTp/qPFoGqCtOUsALR5sTr2bL8LOSyKM2qxclUnQU81tiTWxrN29T7Egji39pl2aR6AAAA3ElEQVQYGQXBA4IDUAxAwdet7XZt27Zt22aSf/+dAQAAAACAqdVk8HBvOwUAeNMrYiZqhYQfwDthameh88sT50IBIG36+3IPnB6LTMJIwZ5ev4p/R1cXLIvmWXCfD/F3+xHZX1uakzG25JpnyX8fOFlkXIfIyR0fb5ARnaVoHnKywc1jNhJUHWVQe+lz0c3b4O60SmdJbEA6SIjNr+8wLNJEo0qYKlWTGWLd7VRGRWug3Kmm+jOtzQ0e1TDgD2mwp0tMTVxpBCBQn8y2iYnTsjgAUFvS4vPVVVcA/AMRETlXnYR1ZQAAAABJRU5ErkJggg==");background-position:2px 1px}.social-likes__counter_pinterest{background:#fff5f6;border-color:#f0a8ae}.social-likes__counter_pinterest:after{border-right-color:#fff5f6} ================================================ FILE: atom.xml ================================================ --- layout: nil title : Atom Feed --- {{ site.author }} - {{ site.title }} {{ site.time | date_to_xmlschema }} {{ site.production_url }} {{ site.author }} «{{ site.title }}» anton_shuvalov@me.com {% for post in site.posts %} {% if post.published %} {{ post.title }} {{ post.date | date_to_xmlschema }} {% if post.extlink %} {% else %} {{ site.production_url }}{{ post.id }} {{ post.content | xml_escape }} {% endif %} {% endif %} {% endfor %} ================================================ FILE: epub/_source.md ================================================ % Паттерны для масштабируемых JavaScript-приложений % Эдди Османи ## Вступление В этой книге мы обсудим набор паттернов, который поможет вам в создании больших масштабируемых JavaScript-приложений. Материал книги основан на моем одноименном докладе, впервые прочитанном на конференции «LondonJS», и вдохновленном [предшествующей ему работой][01] Николаса Закаса. ### Кто я и почему я решил об этом написать? Меня зовут Эдди Османи. Сейчас я работаю JavaScript- и UI-разработчиком в AOL. Я занимаюсь планированием и написанием фронтенд-архитектуры для следующего поколения наших пользовательских приложений. Эти приложения весьма сложны. Они нуждаются в архитектуре, позволяющей, с одной стороны легко их масштабировать, а с другой достаточно легко использовать повторно их модули. Также я занимаюсь разработкой шаблонов, которые можно применять в разработке приложений подобного масштаба настолько качественно, насколько это вообще возможно. Кроме того, я рассматриваю себя как евангелиста шаблонов проектирования (хотя есть много экспертов, разбирающихся в этом лучше меня). В прошлом я написал книгу «[Essential JavaScript Design Patterns][02]», а сейчас я занимаюсь написанием более подробного продолжения этой книги. ### Могу ли я уместить эту книгу в 140 символов? Я уместил эту статью в один твит, на случай, если у вас совсем мало времени: Меньше связанности: используйте паттерны «модуль», «фасад» и «медиатор». Модули общаются через медиатор, а фасад обеспечивает безопасность. [01]: http://yuilibrary.com/theater/nicholas-zakas/zakas-architecture/ [02]: http://addyosmani.com/resources/essentialjsdesignpatterns/book/ ## Что из себя представляет «большое» JavaScript приложение? Перед тем как я начну, давайте постараемся определить, что именно мы имеем в виду, когда говорим о больших JavaScript-приложениях. Этот вопрос я считаю своего рода вызовом опытным разработчикам, и ответы на него, соответственно, получаются очень субъективными. Ради эксперимента я предложил нескольким среднестатистическим разработчикам дать собственное определение этому термину. Один из разработчиков сказал, что речь идет о «JavaScript-приложениях, состоящих из более чем 100 000 строк кода», когда другой определил что большое приложение «содержит больше чем 1 МБ JavaScript». Я расстроил храбрецов — оба этих варианта далеки от истины. Количество кода не всегда коррелирует со сложностью приложения. 100 000 строк легко могут оказаться самым ничем не примечательным простым кодом. Я не знаю, подходит ли мое собственное определение к любому случаю, но я верю, что оно находится ближе всего к тому, что действительно представляет из себя большое JavaScript-приложение. Я думаю, что большие JavaScript-приложения решают нетривиальные задачи, а поддержка таких приложений требует от разработчика серьезных усилий. При этом, большая часть работы по манипуляции данными и их отображению ложится на браузер. Я думаю, что последняя часть определения — самая важная. ## Давайте обсудим вашу существующую архитектуру Работая над большим JavaScript-приложением, не забывайте уделять **достаточное количество времени** на планирование изначальной архитектуры, к которой такие приложения очень чувствительны. Большие приложения обычно представляют из себя очень сложные системы, гораздо более сложные, чем вы представляете себе изначально. Я должен подчеркнуть значение этой разницы — я видел разработчиков, которые, сталкиваясь с большими приложениями, делали шаг назад и говорили: «Хорошо, у меня есть несколько идей, которые хорошо показали себя в моем предыдущем проекте среднего масштаба. Думаю, они точно сработают и для чего-то большего, не так ли?». Конечно, до какого-то момента это может быть так, но, пожалуйста, не принимайте это как должное — по большей части большие приложения имеют ряд достаточно серьезных проблем, с которыми нужно считаться. Ниже я приведу несколько доводов в пользу того, почему вам стоит уделить немного больше времени планированию архитектуры своего приложения, и чем вам это будет полезно в долгосрочной перспективе. Большинство JavaScript-разработчиков в архитектуре своих приложений обычно использует различные комбинации следующих компонентов: * виджеты * модели * представления * контроллеры * шаблоны * библиотеки * ядро приложения. **Ссылки по теме:** [Ребекка Мёрфи — Создание архитектуры JavaScript-приложений][2-1] [Питер Мишо — MVC архитектура для JavaScript-приложений][2-2] [StackOverflow — Дискуссия о современных MVC-фреймворках][2-3] [Дуг Найнер — Поддерживаемые плагины и фабрика виджетов][2-4] Вероятно, вы выносите различные функции ваших приложений в отдельные модули, либо используете какие-нибудь другие шаблоны проектирования для подобного разделения. Это очень хорошо, но здесь есть ряд потенциальных проблем, с которыми вы можете столкнуться при таком подходе. ### 1. Готова ли ваша архитектура к повторному использованию кода уже сейчас? Могут ли отдельные модули использоваться самостоятельно? Достаточно ли они автономны для этого? Мог бы я прямо сейчас взять один из модулей вашего большого приложения, просто поместить его на новую веб-страницу, а затем, тут же, начать его использовать? У вас может возникнуть вопрос: «Действительно ли это так необходимо?», но, как бы то ни было, я надеюсь, что вы думаете о будущем. Что, если ваша компания начнет создавать все больше и больше нетривиальных приложений, которые будут иметь некоторую общую функциональность? Если кто-то скажет: «Нашим пользователям очень нравится использовать модуль чата в нашем email-клиенте, почему бы нам не добавить этот модуль к нашему новому приложению для совместной работы с документами?», будет ли это возможно, если мы не уделим должного внимания контролю кода? ### 2. Сколько модулей в вашей системе зависит от других модулей? Насколько сильно связаны ваши модули? Перед тем, как я погружусь в объяснения о том, как важна слабая связанность модулей, я должен отметить, что не всегда есть возможность создавать модули, не имеющие абсолютно никаких зависимостей в системе. К примеру, одни модули могут расширять функции других, уже существующих. Эта тема, скорее всего, относится к группировке модулей на основе некоторой функциональности. Отдельные наборы модулей должны работать в вашем приложении без большого количества зависимостей, чтобы наличие или загрузка других модулей не влияла на их работоспособность. ### 3. Сможет ли ваше приложение работать дальше, если его отдельная часть сломается? Если вы разрабатываете приложения, подобные Gmail, и ваш webmail-модуль (или группа модулей) перестанет работать из-за ошибки, то это не должно заблокировать пользовательский интерфейс или помешать пользователям использовать другие части вашего приложения, к примеру, такие как чат. В то же время, как было сказано раньше, было бы идеально, если бы модули могли работать и за пределами вашей архитектуры. В моей лекции я упоминал динамические зависимости — возможность загружать модули, исходя из определенных действий пользователя. К примеру, ребята из Gmail могли бы изначально держать чат закрытым, не загружая его код при открытии страницы. А в тот момент, когда пользователь решит воспользоваться им — соответствующий модуль будет динамически загружен и выполнен. В идеальном случае хотелось бы выполнить это без каких-то негативных эффектов в вашем приложении. ### 4. Насколько легко вы сможете тестировать отдельные модули? Когда вы работаете над масштабными системами, есть вероятность, что различные части этой системы будут использовать миллионы пользователей. Вполне вероятно, что эти части будут использоваться не только в предусмотренных вами ситуациях. В конечном счете, код может использоваться повторно в огромном количестве различных окружений, и важно, чтобы модули были достаточно протестированы. Тестировать модули необходимо и внутри архитектуры, для которой он был изначально разработан, и снаружи. По моему мнению, это дает наибольшую гарантию того, что модуль не сломается при попадании в другую систему. [2-1]: http://rmurphey.com/blog/2010/08/27/code-org-take-2-structuring-javascript-applications/ [2-2]: http://michaux.ca/articles/mvc-architecture-for-javascript-applications [2-3]: http://stackoverflow.com/questions/5112899/knockout-js-vs-backbone-js-vs [2-4]: http://msdn.microsoft.com/en-us/scriptjunkie/ff706600 ## Думай о будущем В процессе создания архитектуры большого приложения, очень важно думать о будущем. Не только о том, что будет через месяц или через год, но и о том, что будет после этого. Что может измениться? Конечно, невозможно достаточно точно предсказать как ваше приложение будет развиваться, но, вне всякого сомнения, имеет смысл подумать об этом. Думаю, что найдется, хотя бы один специфичный аспект вашего приложения, о котором стоит поразмыслить. Разработчики зачастую слишком сильно связывают манипуляцию с DOM-элементами и остальные части приложения, даже если до этого они не поленились разделить бизнес-логику на модули. Подумайте, почему в долгосрочной перспективе это может быть плохой идеей? Один из слушателей моей лекции предположил, что такая архитектура негибка, и может не работать в будущем. Это действительно так, но есть другая проблема, игнорирование которой окажет еще более негативный эффект. В будущем, вы можете принять решение о **замене** Dojo, jQuery, Zepto или YUI на что-нибудь совершенно иное. Причиной такого перехода может быть производительность, безопасность или дизайн. Это может стать серьезной проблемой, потому как библиотеки не предусматривают простой замены. Цена замены библиотеки будет высокой, если ваше приложение тесно с ней связано. Если вы используете Dojo (как многие слушатели на моей лекции), вы можете быть уверены, что нет ничего лучше, на что имело бы смысл сейчас перейти. Но можете ли вы быть уверены, что в течении двух-трех лет не появится что-нибудь, что вызовет у вас интерес? Что-нибудь, на что вы можете решить перейти. Такое решение может быть достаточно простым для небольших проектов, но **не для больших приложений**, архитектура которых, в целом, достаточна их поддержки. Такое решение может быть достаточно простым для небольших проектов, но на **больших приложениях** с непродуманной архитектурой всю тяжесть смены библиотеки вы ощутите сразу. Подводя итог, взгляните на вашу архитектуру сейчас. Сможете ли вы сегодня сменить вашу библиотеку на любую другую, не переписывая при этом ваше приложение полностью? Если это не так, то вам стоит продолжить чтение. Я думаю, что в архитектуре, которую мы обсуждаем, вы найдёте кое-что интересное. Некоторые из известных JavaScript-разработчиков раньше уже излагали проблемы, о которых я написал выше. Вот три ключевых цитаты, которыми я бы хотел поделиться с вами. «Секрет создания больших приложений в том, чтобы никогда не создавать больших приложений. Разбейте ваши приложения на маленькие части, а затем собирайте из этих маленьких тестируемых фрагментов ваше большое приложение» **Джастин Майер, автор «JavaScriptMVC»** «Секрет в том, чтобы признаться самому себе с самого начала, что вы понятия не имеете о том, как ваше приложение будет развиваться. Когда вы согласитесь с этим, вы начнете проектировать систему основываясь на защите. Вы определите ключевые области, в которых, вероятнее всего будут происходить изменения. Очень часто это не составляет труда, если потратить на это немного времени. К примеру, вы ожидаете, что любая часть приложения, которая взаимодействует с другой системой — это потенциальная мишень для изменений. И вы понимаете, что здесь вам понадобится абстракция». **Николас Закас, автор книги «Высокопроизводительный JavaScript** И последняя, но тоже очень важная цитата: «Чем сильнее компоненты связаны между собой, тем меньше возможностей для их повторного использования, тем сложнее вносить изменения, не получая при этом различных побочных эффектов в самых неожиданных местах» **Ребекка Мёрфи, автор книги «Фундаментальные основы jQuery»** Эти принципы необходимы для создания архитектуры, способной выдержать испытание временем. Важно всегда помнить о них. ## Мозговой штурм Давайте немного подумаем, что мы хотим получить. Мы хотим получить слабосвязанную архитектуру с функциональностью, разделенную на **независимые модули**, которые, в идеале, не должны иметь зависимостей друг от друга. Когда случается что-то интересное, модули **сообщают** об этом другим частям приложения, а промежуточный слой интерпретирует их сообщения и необходимым образом реагирует на них. Для примера, у нас есть JavaScript-приложение, отвечающее за онлайн-пекарню. Одно из интересных нам сообщений может быть таким: «Партия из 42 батонов готова к доставке». Мы используем отдельный слой для обработки сообщений модулей, чтобы а) модули не взаимодействовали напрямую с ядром, б) модули не взаимодействовали напрямую друг с другом. Это помогает не допустить падения приложения из-за различных ошибок внутри одного из модулей. Также это позволяет нам перезапускать модули если они вдруг перестали работать из-за какой-нибудь ошибки. Еще один момент — безопасность. На самом деле, немногие из нас заботятся о внутренней безопасности своих приложений в должной мере. Когда мы определяем структуру приложения, мы говорим себе, что мы достаточно умны для того, чтобы понимать что в нашем коде должно быть публичным, а что приватным. Хорошо, но поможет ли это если вы решите определить что именно разрешено модулю выполнять в системе? К примеру, в моем приложении, ограничив доступ из модуля веб-чата к интерфейсу модуля администрирования, я смогу уменьшить шансы на успешное использование XSS уязвимостей, которые я не смог найти в виджете. Модули не должны иметь доступ ко всему. Вероятно, в вашей существующей архитектуре они могут использовать любые части системы, но уверены ли вы, что это действительно необходимо? Промежуточный слой, проверяющий имеет ли модуль доступ к определенной части вашего фреймворка, обеспечивает большую безопасность вашей системы. Фактически, это значит что модули могут взаимодействовать только с теми компонентами системы, с которыми мы разрешим им взаимодействовать. ### Архитектура, которую я предлагаю вам Архитектура, о которой мы говорим, представляет из себя комбинацию трех известных шаблонов проектирования: модуль, фасад и медиатор. В отличии от традиционной модели, в которой модули напрямую взаимодействуют друг с другом, в этой слабосвязанной архитектуре модули всего лишь публикуют события (в идеале, не зная о других модулях в системе). Медиатор используется для подписки на сообщения от модулей и для решения, каким должен быть ответ на уведомление. Паттерн фасад используется для ограничения действий разрешенных модулям. В следующих главах я более детально расскажу о каждом из этих шаблонов проектирования. ## Теория модулей Вероятно, в каком-то виде вы уже используете модули в своей существующей архитектуре. Если это не так, то в этой главе я покажу вам, как они устроены. Модули — это **целая** часть любой хорошей архитектуры приложения. Обычно модули выполняют одну определенную задачу в более крупных системах и могут быть взаимозаменяемы. В некоторых реализациях модули могут иметь свои собственные зависимости, которые будут загружены автоматически, собирая вместе таким образом все компоненты системы. Такой подход считается более масштабируемым, в отличие от ручной загрузки модулей или подстановки тега `script`. Каждое нетривиальное приложение должно создаваться из модульных компонентов. Рассмотрим GMail: вы можете рассматривать модули, как независимые единицы функциональности, которые могут и должны существовать сами по себе; возьмём к примеру чат. Скорее всего он основан на своём отдельном модуле чата, но так как этот модуль скорее всего очень сложный, то он вероятно состоит из более мелких вспомогательных модулей. Например, один из таких модулей мог бы отвечать за использование смайликов и он же мог бы использоваться не только в чате, но также и в почте. В рассматриваемой архитектуре модули имеют **очень ограниченные знания** о том, что происходит в других частях системы. Вместо этого мы делегируем ответственность медиатору и фасаду. В этом и заключается идея нашей архитектуры — если модуль заботится исключительно о том, чтобы уведомить систему об интересующих ее происшествиях, и не волнуется запущены ли другие модули, то система может добавлять, удалять или заменять одни модули, не ломая при этом другие, что было бы невозможно при сильной связанности. Слабая связанность — необходимое условие для того, чтобы такая идея была возможна. Она делает поддержку модулей проще, удаляя зависимости в коде там, где это возможно. В вашем случае, одни модули должны работать корректно в не зависимости от того в каком порядке загрузились другие модули. Когда слабая связанность реализована эффективно, становится очевидно, как изменения в одной части системы влияют на другие ее части. В JavaScript есть несколько мнений о том, как могут быть реализованы модули, включая шаблон «Модуль» и Object Literal (литеральная запись объекта `var obj = {};`). Опытные разработчики должно быть уже знакомы с ними. Если это так, то вы можете пропустите следующую главу и перейти сразу к главе «CommonJS Modules». ## Паттерн «Модуль» «Модуль» — это популярная реализация паттерна, инкапсулирующего приватную информацию, состояние и структуру, используя замыкания. Это позволяет оборачивать публичные и приватные методы и переменные в модули, и предотвращать их попадание в глобальный контекст, где они могут конфликтовать с интерфейсами других разработчиков. Паттерн «модуль» возвращает только публичную часть API, оставляя всё остальное доступным только внутри замыканий. Это хорошее решение для того, чтобы скрыть внутреннюю логику от посторонних глаз и производить всю тяжелую работу исключительно через интерфейс, который вы определите для использования в других частях вашего приложения. Этот паттерн очень похож на немедленно-вызываемые функции ([IIFE][6-3]), за тем исключением, что модуль вместо функции, возвращает объект. Важно заметить, что в JavaScript нет настоящей приватности. В отличии от некоторых традиционных языков, он не имеет модификаторов доступа. Переменные технически не могут быть объявлены как публичные или приватные, и нам приходится использовать область видимости для того, чтобы эмулировать эту концепцию. Благодаря замыканию, объявленные внутри модуля переменные и методы доступны только изнутри этого модуля. Переменные и методы, объявленные внутри объекта, возвращаемого модулем, будут доступны всем. Ниже вы можете увидеть корзину покупок, реализованную с помощью паттерна «модуль». Получившийся компонент находится в глобальном объекте `basketModule`, и содержит всё, что ему необходимо. Находящийся внутри него, массив `basket` приватный, и другие части вашего приложения не могут напрямую взаимодействовать с ним. Массив `basket` существует внутри замыкания, созданного модулем, и взаимодействовать с ним могут только методы, находящиеся в том же контексте (например, `addItem()`, `getItem()`). ``` var basketModule = (function() { var basket = []; // приватная переменная return { // методы доступные извне addItem: function(values) { basket.push(values); }, getItemCount: function() { return basket.length; }, getTotal: function() { var q = this.getItemCount(),p=0; while(q--){ p+= basket[q].price; } return p; } } }()); ``` Внутри модуля, как вы заметили, мы возвращаем объект. Этот объект автоматически присваивается переменной `basketModule`, так что с ним можно взаимодействовать следующим образом: ``` // basketModule - это объект со свойствами, которые могут также быть и методами: basketModule.addItem({item:'bread', price:0.5}); basketModule.addItem({item:'butter', price:0.3}); console.log(basketModule.getItemCount()); console.log(basketModule.getTotal()); // А следующий ниже код работать не будет: console.log(basketModule.basket); // undefined потому что не входит в возвращаемый объект console.log(basket); // массив доступен только из замыкания ``` Методы выше, фактически, помещены в неймспейс `basketModule`. Исторически, паттерн «модуль» был разработан в 2003 году группой людей, в число которых входил [Ричард Корнфорд][6-4]. Позднее, этот паттерн был популяризован Дугласом Крокфордом в его лекциях, и открыт заново в блоге YUI благодаря Эрику Мирагилиа. Давайте посмотрим на реализацию «модуля» в различных библиотеках и фреймворках. **Dojo** Dojo старается обеспечивать поведение похожее на классы с помощью `dojo.declare`, который, кроме создания «модулей», также используется и для других вещей. Давайте попробуем, для примера, определить `basket` как модуль внутри неймспейса `store`: ``` // традиционный способ var store = window.store || {}; store.basket = store.basket || {}; // с помощью dojo.setObject dojo.setObject("store.basket.object", (function() { var basket = []; function privateMethod() { console.log(basket); } return { publicMethod: function() { privateMethod(); } }; }())); ``` Лучшего результата можно добиться, используя `dojo.provide` и миксины. **YUI** Следующий код, по большей части, основан на примере реализации паттерна «модуль» в фреймворке YUI, разработанным Эриком Миргалиа, но более самодокументирован. ``` YAHOO.store.basket = function () { // приватная переменная: var myPrivateVar = "Ко мне можно получить доступ только из YAHOO.store.basket."; // приватный метод: var myPrivateMethod = function() { YAHOO.log("Я доступен только при вызове из YAHOO.store.basket"); } return { myPublicProperty: "Я - публичное свойство", myPublicMethod: function() { YAHOO.log("Я - публичный метод"); // Будучи внутри корзины я могу получить доступ к приватным переменный и методам: YAHOO.log(myPrivateVar); YAHOO.log(myPrivateMethod()); // Родной контекст метода myPublicMethod сохранён // поэтому мы имеет доступ к this YAHOO.log(this.myPublicProperty); } }; }(); ``` **jQuery** Существует множество способов, чтобы представить jQuery-код в виде паттерна «модуль», даже если этот код не напоминает привычные jQuery-плагины. Бен Черри ранее предлагал способ, при котором, если у модулей есть общие черты, то они объявляются через функцию-обертку. В следующем примере функция `library` используется для объявления новой библиотеки и, автоматически, при создании библиотеки (т.е. модуля), связывает вызов метода `init` с `document.ready`. ``` function library(module) { $(function() { if (module.init) { module.init(); } }); return module; } var myLibrary = library(function() { return { init: function() { /* код модуля */ } }; }()); ``` **Ссылки по теме:** [Бен Черри — Погружение в паттерн «Модуль»][6-5] [Джон Ханн — Будущее — это модули, а не фреймворки][6-6] [Натан Смит — Ссылки на window и document в модулях (gist)][6-7] [6-3]: http://benalman.com/news/2010/11/immediately-invoked-function-expression/ [6-4]: http://groups.google.com/group/comp.lang.javascript/msg/9f58bd11bd67d937 [6-5]: http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth [6-6]: http://lanyrd.com/2011/jsconf/sfgdk/ [6-7]: https://gist.github.com/274388 ## Литеральная нотация объекта В литеральной нотации объект описывается внутри блока фигурных скобок (`{}`), как набор разделенных запятой пар ключ/значение. Ключи объекта могут быть как строками, так и идентификаторами. После имени ставится двоеточие. В объекте не должно стоять запятой после последней пары ключ/значение, так как это может привести к ошибкам. Литерал объекта не требует использования оператора `new` для создания экземпляра, но он не должен стоять в начале выражения, так как открытая `{` может быть воспринята как начало блока. Ниже вы можете увидеть пример модуля, определенного с помощью литеральной нотации объекта. Новые члены объекта могут быть добавлены с помощью конструкции `myModule.property = 'someValue';` Паттерн «модуль» может быть полезен для многих вещей. Но если вы считаете, что вам не нужно делать приватными некоторые методы или свойства, то литерал объекта — более чем подходящий выбор. ``` var myModule = { myProperty: 'someValue', // Литералы объектов могут содержать свойства и методы. // ниже в свойстве определен другой объект, // для описания конфигурации: myConfig: { useCaching: true, language: 'en' }, // Очень простой метод myMethod: function() { console.log('I can haz functionality?'); }, // вывод значения заданного в конфигурации myMethod2: function() { console.log('Caching is: ' + ((this.myConfig.useCaching) ? 'enabled' : 'disabled')); }, // переопределение конфигурации myMethod3: function(newConfig) { if (typeof newConfig == 'object') { this.myConfig = newConfig; console.log(this.myConfig.language); } } }; myModule.myMethod(); // 'I can haz functionality' myModule.myMethod2(); // Вывод 'enabled' myModule.myMethod3({language:'fr',useCaching:false}); // 'fr' ``` **Ссылки по теме:** [Ребекка Мёрфи — Использование объектов для организации вашего кода][7-1] [Стоян Стефанов — 3 способа определения класса в JavaScript ][7-2] [Бен Алман — Разъяснения по литералам объектов (понятия JSON-объект не существует)][7-3] [Джон Резиг - Простое наследование в JavaScript][7-4] [7-1]: http://blog.rebeccamurphey.com/2009/10/15/using-objects-to-organize-your-code [7-2]: http://www.phpied.com/3-ways-to-define-a-javascript-class/ [7-3]: http://benalman.com/news/2010/03/theres-no-such-thing-as-a-json/ [7-4]: http://ejohn.org/blog/simple-javascript-inheritance/ ## CommonJS Модули Возможно, вы что-то слышали о [CommonJS][8-4] за последние пару лет. CommonJS — это добровольная рабочая группа, которая проектирует, прототипирует и стандартизирует различные JavaScript API. На сегодняшний день они ратифицировали стандарты для модулей и пакетов — CommonJS определяют простой API для написания модулей, которые могут быть использованы в браузере с помощью тега `